我忘记了所有悲剧,所见皆是奇迹。|

Gdfzlcx

园龄:2年6个月粉丝:0关注:0

P2540 [NOIP2015 提高组] 斗地主加强版题解

题目描述

给出n张牌,求出打光n牌的最少次数,详情请看题面

分析

考虑出牌是我们要分两种情况:顺子散牌
应为只有顺子要考虑点数,散牌不需要。

顺子

爆搜即可
Q:有人可能会问直接求不香吗?
A:直接算最长的并不是最优解。
eg.
有点数为:3,4,5,6,6,7,7,8,9,10
那最长的会多两个单,但我们可以{3,4,5,6,7},{6,7,8,9,10}

散牌

dp即可
设dp[a][b][c][d][e]为a张单牌,b张对,c对三连,d个炸弹,e张王的状态。
eg.
单张可以从dp[a-1][b][c][d][e]转移过来
三带一可以从dp[a-1][b][c-1][d][e]转移过来

代码

是不是挺简单的。
前方高能码风

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int t,n,a,b,ans;
int p[20],dp[20][20][20][20][5],cnt[10],st[10]={0,5,3,2};
void init()
{
	memset(dp,0x3f,sizeof(dp));
	dp[0][0][0][0][0]=0;
	for(int d=0;d<=15;d++)
		for(int c=0;c<=15;c++)
			for(int a=0;a<=15;a++)
				for(int b=0;b<=15;b++)
					for(int e=0;e<=2;e++)
					{	
						int minn=0x3f;
						if(a>0)minn=min(minn,dp[a-1][b][c][d][e]+1);
						if(b>0)minn=min(minn,dp[a][b-1][c][d][e]+1);
						if(c>0)minn=min(minn,dp[a][b][c-1][d][e]+1);
						if(d>0)minn=min(minn,dp[a][b][c][d-1][e]+1);
						if(e>0)minn=min(minn,dp[a][b][c][d][e-1]+1);
						if(b>0&&d>0)minn=min(minn,dp[a][b-1][c][d-1][e]+1);
						if(d>0&&e>1)minn=min(minn,dp[a][b][c][d-1][e-2]+1);
						if(b>1&&d>0)minn=min(minn,dp[a][b-2][c][d-1][e]+1);
						if(d>1)minn=min(minn,dp[a][b][c][d-2][e]+1);
						if(a>1&&d>0)minn=min(minn,dp[a-2][b][c][d-1][e]+1);
						if(a>0&&d>0&&e>0)minn=min(minn,dp[a-1][b][c][d-1][e-1]+1);
						if(c>0&&e>0)minn=min(minn,dp[a][b][c-1][d][e-1]+1);
						if(a>0&&c>0)minn=min(minn,dp[a-1][b][c-1][d][e]+1);
						if(b>0&&c>0)minn=min(minn,dp[a][b-1][c-1][d][e]+1);
						if(c>0)minn=min(minn,dp[a+1][b+1][c-1][d][e]);
						if(d>0)minn=min(minn,dp[a+1][b][c+1][d-1][e]);
						if(e>1)minn=min(minn,dp[a][b][c][d][e-2]+1);
						dp[a][b][c][d][e]=min(dp[a][b][c][d][e],minn); 
					}	
}
void dfs(int x)
{
	if(x>=ans)return ;
	for(int k=1;k<=3;k++)
		for(int i=1;i<=12;i++)
		{
			int l=0;
			while(i+l<=12&&p[i+l]>=k)l++;
			for(int j=l;j>=st[k];j--)
			{
				for(int t=i;t<=i+j-1;t++)p[t]-=k;
				dfs(x+1);
				for(int t=i;t<=i+j-1;t++)p[t]+=k;
			}
		}
	memset(cnt,0,sizeof(cnt));
	for(int i=1;i<=13;i++)cnt[p[i]]++;
	cnt[5]=p[14];
	ans=min(ans,x+dp[cnt[1]][cnt[2]][cnt[3]][cnt[4]][cnt[5]]);
}
int main()
{
	scanf("%d%d",&t,&n);
	init();
	while(t--)
	{
		ans=n;
		memset(p,0,sizeof(p));
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d",&a,&b);
			if(a==0)p[14]++;
			else if(a==1)p[12]++;
			else if(a==2)p[13]++;
			else p[a-2]++;
		}
		dfs(0);
		printf("%d\n",ans);
	}
	return 0;
} 

本文作者:Gdfzlcx

本文链接:https://www.cnblogs.com/gdfzlcx/p/16639915.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Gdfzlcx  阅读(100)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起