bzoj1860 [Zjoi2006]麻将 结论+插数dp

这个题模型+手玩+特殊情况  可以比较简单的推出

首先考虑特殊情况 即这个单独的两个,这是可以枚举的

然后考虑模型,根据插数dp的思考方式,插入一个数会造成的影响只与相邻两个位置有关,所以就可以找到状态

但是f[100][100][100]的状态在扫的时候会炸,所以就考虑优化


手玩一下,发现同一副牌可以有多种胡的方式,此时就考虑怎么往一个方向上靠,然后发现3个3张和3个顺是一样的

而状态只和顺有关,所以就只需要存一小部分去转移即可,其他的就作为 3个相同或4个相同的打出


码:

#include
#include
#include
using namespace std;
int f[101][30][30],g[101][101],a[101],i,j,k,l,p,T,yes;
int main()
{
	scanf("%d",&T);
	while(T--)
	{
	yes=0;
		for(i=1;i<=100;i++)
			scanf("%d",&a[i]);
		for(p=1;p<=100;p++)
		{
			if(a[p]<2)continue;
			a[p]-=2;
      memset(f,0,sizeof(f));
	  	f[0][0][0]=1;
			for(i=1;i<=100;i++)
			{	  
for(j=0;j<=a[i];j++)g[i][j]=0;
g[i][a[i]]=1;			
				//可以的余剩	
			for(j=a[i];j>=0;j--) 
			{		
			if(j>=4)g[i][j-4]=max(g[i][j],g[i][j-4]);
			if(j>=3) g[i][j-3]=max(g[i][j],g[i][j-3]);
		    }
		  //dp
		 for(j=0;j<=min(10,a[i]);j++)//当前拿几个 
		   {
		   	if(g[i][j])
		for(k=0;k<=min(10,a[i-1]);k++)//要求 
		for(l=0;l<=k;l++)   	
		{
		if(f[i-1][l][k]&&j>=k)
		{
				f[i][k-l][j-l]=max(f[i][k-l][j-l],f[i-1][l][k]);
		}	
		} 	
		   }
		   } 	
			if(f[100][0][0])
			{
			printf("Yes\n");yes=1;
			break;	
			}
			a[p]+=2;
		}
		if(yes==0)printf("No\n");
	}
}



你可能感兴趣的:(题目)