您的位置:首页 > 其它

【dfs+贪心】【cofun1624】斗地主

2017-10-13 20:59 351 查看

【cofun1624】斗地主

Description



Input Format

输入文件名为 landlords.in。

第一行包含用空格隔开的2个正整数T,n,表示手牌的组数以及每组手牌的张数。

接下来T组数据,每组数据n行,每行一个非负整数对ai,bi,表示一张牌,其中ai表示牌的数码,bi表示牌的花色,中间用空格隔开。特别的,我们用 1 来表示数码 A,11 表示数码 J,12 表示数码 Q,13 表示数码 K;黑桃、红心、梅花、方片分别用 1-4 来表示;小王的表示方法为 0 1,大王的表示方法为 0 2。

Output Format

输出文件名为 landlords.out。 共 T 行,每行一个整数,表示打光第i组手牌的最少次数。

Sample Input

【输入样例 1】

1 8

7 4

8 4

9 1

10 4

11 1

5 1

1 4

1 1

【输入样例 2】

1 17

12 3

4 3

2 3

5 4

10 2

3 3

12 2

0 1

1 3

10 1

6 2

12 1

11 3

5 2

12 4

2 2

7 2

Sample Output

输出样例 1

3

输出样例 2

6

【输入输出样例 1 说明】

共有 1 组手牌,包含 8 张牌:方片7,方片8,黑桃9,方片10,黑桃J,黑桃5,方片A 以及黑桃A。可以通过打单顺子(方片7,方片8,黑桃9,方片10,黑桃J),单张牌(黑桃5)以及对子牌(黑桃A 以及方片A)在3次内打光。

Hint



分析:

可以把王炸视为对子,因此花色与问题本身并无关系,牌类种数<=14。

显然只有顺子对出牌顺序产生影响,而三张、对子、单张可以贪心出最少的出牌数。

考虑爆搜+剪枝。

dfs减去顺子.

剪枝中贪心出其他的出牌数,加上当前步数若> ans,则直接 return.

具体实现见代码→

代码:

#include <bits/stdc++.h>
using namespace std;

int T, n, a, b, i, num[15], cou[15], ans;

inline int tan()
{
memset(cou, 0, sizeof(cou));
int t = 0;

for(int i = 1; i <= 14; i ++) cou[num[i]] ++;
if (cou[4] && (cou[2] > 1 || cou[1] > 1))
{
if (cou[2] / 2 <= cou[4])
t += cou[2] / 2, cou[4] -= cou[2] / 2, cou[2] %= 2;
else
t += cou[4], cou[2] -= cou[4] * 2, cou[4] = 0;//四带二对子
if (cou[1] / 2 <= cou[4])
t += cou[1] / 2, cou[4] -= cou[1] / 2, cou[1] %= 2;
else
t += cou[4], cou[1] -= cou[4] * 2, cou[4] = 0;//四带二单牌
}
if (cou[3] && (cou[2] || cou[1]))
{
if (cou[2] <= cou[3])
t += cou[2], cou[3] -= cou[2], cou[2] = 0;
else
t += cou[3], cou[2] -= cou[3], cou[3] = 0;//三带二
if (cou[1] <= cou[3])
t += cou[1], cou[3] -= cou[1], cou[1] = 0;
else
t += cou[3], cou[1] -= cou[3], cou[3] = 0;//三带一
}
t += cou[4] + cou[3] + cou[2] + cou[1];//炸弹&三张牌&对子牌&单张牌

return t;
}
//贪心出打完各种牌的最少次数
inline void dfs(int s)
{
if (s > ans) return;
ans = min(ans, s + tan());

int i, j, k, l;
for(i = 3; i <= 13; i ++)
{
for(j = i; num[j] >= 3; j ++);
if (j - i < 2) continue;
for(k = i + 1; k < j; k ++)
{
for(l = i; l <= k; l ++) num[l] -= 3;
dfs(s + 1);
for(l = i; l <= k; l ++) num[l] += 3;
}
}//三顺子
for(i = 3; i <= 12; i ++)
{
for(j = i; num[j] >= 2; j ++);
if (j - i < 3) continue;
for(k = i + 2; k < j; k ++)
{
for(l = i; l <= k; l ++) num[l] -= 2;
dfs(s + 1);
for(l = i; l <= k; l ++) num[l] += 2;
}
}//二顺子
for(i = 3; i <= 10; i ++)
{
for(j = i; num[j] >= 1; j ++);
if (j - i < 5) continue;
for(k = i + 4; k < j; k ++)
{
for(l = i; l <= k; l ++) num[l] --;
dfs(s + 1);
for(l = i; l <= k; l ++) num[l] ++;
}
}//单顺子

return;
}
//搜索打出顺子
int main()
{

for(scanf("%d%d", &T, &n); T; T --)
{
memset(num, 0, sizeof(num));
ans = 0;
//初始化
for(i = 1; i <= n; i ++)
{
scanf("%d%d", &a, &b);
if (a == 1) a = 14;
if (a == 0) a = 1;
num[a] ++;
ans ++;
}
//预处理牌,花色无用
dfs(0);
//爆搜
printf("%d\n", ans);
}

return 0;

}


【老师在讲初赛的题目QAQ做不下去题目,来啪啪题解。

这题被cofun归在状压DP中,还蛮奇怪的233是因为有状压的做法吗?把各种打牌顺序压位,把牌的情况压位,把一种牌的张数另存?貌似可行??ε=(´ο`*)))唉其实搜索和DP相似之处好多,比如都有状态,都要转移,而且很多hash还蛮像【??】

2333坐等放学~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  搜索 贪心