斗地主题解
由于本人特别会打斗地主,所以此题我在普通版本的代码稍加修改(也就是大小王不能当对这处),然后通过了加强版。
我没有考虑怎么贪心甚至一开始我都不知道从哪下手开始搜索,那先考虑暴力搜,也就是每张牌的出牌形式。
剪枝:
-
必须有序我们发现最小出牌方式和出牌的顺序无关我们强制一定的顺序。这里的提现是由小到大出牌。把小的出完再出大的。
-
最优化剪枝。也就是如果最优解比当前层数要浅就停止。
-
当前有牌且深度+1==最优解 直接停止。
其实我们发现第一种是核心剪枝必须有序才行。
第二,三种是必要的剪枝。
还有一点需要注意的是发现前面的单牌可以和后面的一些牌进行操作我们必须考虑到,这并不浪费时间。
这样我们顺利通过了这道题不用贪心啦,拆牌的考虑辣么多。
//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 10000000000000ll
#define ll long long
#define l(x) t[x].l
#define r(x) t[x].r
#define v(x) t[x].v
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
ll x=0,f=1;char ch=getc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
return x*f;
}
inline void put(ll x)
{
x<0?putchar('-'),x=-x:0;
ll num=0;char ch[50];
while(x)ch[++num]=x%10+'0',x/=10;
num==0?putchar('0'):0;
while(num)putchar(ch[num--]);
putchar('\n');return;
}
//1 最优化剪枝 2 按顺序剪枝
const int MAXN=30;
int T,n;
int maxx=25,m;
int a[MAXN];
inline void dfs(int x,int depth,int p)
{
if(depth>=maxx)return;
if(p&&depth+1==maxx)return;
if(!p){maxx=min(maxx,depth);return;}
if(!a[x]){dfs(x+1,depth,p);return;}
--a[x];
for(int i=x+1;i<=14;++i)//被动三带一
if(a[i]>2)
{
a[i]-=3;
dfs(x,depth+1,p-4);
a[i]+=3;
}
int flag=0;
for(int i=x+1;i<=14;++i)if(a[i]>3)flag=i;
if(flag)
{
a[flag]-=4;
for(int i=x;i<=15;++i)//被动4带二
if(a[i])
{
--a[i];
dfs(x,depth+1,p-6);
++a[i];
}
a[flag]+=4;
}
dfs(x,depth+1,p-1);//出单张牌
++a[x];
if(x!=2)if(a[x+1]&&a[x+2]&&a[x+3]&&a[x+4]&&x+4<=14)
{
--a[x];--a[x+1];--a[x+2];--a[x+3];--a[x+4];
dfs(x,depth+1,p-5);
int w=x+5;
while(a[w]&&w<=14)//单顺子
{
--a[w];
dfs(x,depth+1,p-(w-x+1));
++w;
}
for(int i=w-1;i>=x;--i)++a[i];
}
if(a[x]>1)//出对子 或者火箭
{
a[x]-=2;
dfs(x,depth+1,p-2);
int flag=0;
for(int i=x+1;i<=14;++i)if(a[i]>3)flag=i;
if(flag)
{
a[flag]-=4;
for(int i=x+1;i<=14;++i)
if(a[i]>1)
{
a[i]-=2;
dfs(x,depth+1,p-8);
a[i]+=2;
}
a[flag]+=4;//被动四代二
}
for(int i=x+1;i<=14;++i)//被动三带二
if(a[i]>2)
{
a[i]-=3;
dfs(x,depth+1,p-5);
a[i]+=3;
}
a[x]+=2;
if(x!=2&&a[x+1]>1&&a[x+2]>1&&x+2<=14)
{
a[x]-=2;a[x+1]-=2;a[x+2]-=2;
int w=x+3;//注意不要加错
dfs(x,depth+1,p-6);
while(a[w]>1&&w<=14)//双顺子
{
a[w]-=2;
dfs(x,depth+1,p-(w-x+1)*2);
++w;
}
for(int i=w-1;i>=x;--i)a[i]+=2;
}
}
if(a[x]>2)
{
a[x]-=3;
dfs(x,depth+1,p-3);//出三张牌
for(int i=x+1;i<=15;++i)
{
if(a[i])
{
--a[i];
dfs(x,depth+1,p-4);//三带一
++a[i];
}
if(a[i]>1&&i<=14)
{
a[i]-=2;
dfs(x,depth+1,p-5);//三带二
a[i]+=2;
}
}
a[x]+=3;
if(a[x+1]>2&&x!=2)//三顺子
{
int w=x+2;//注意两个即可
a[x]-=3;a[x+1]-=3;
dfs(x,depth+1,p-6);
while(a[w]>2)
{
a[w]-=3;
dfs(x,depth+1,p-(w-x+1)*3);
++w;
}
for(int j=w-1;j>=x;--j)a[j]+=3;
}
}
if(a[x]>3)
{
a[x]-=4;
dfs(x+1,depth+1,p-4);//炸弹
for(int i=x+1;i<=15;++i)//终极四带二
for(int j=i;j<=15;++j)
{
if(i==j)//注意相等
{
if(a[i]>1)
{
a[i]-=2;
dfs(x+1,depth+1,p-6);
a[i]+=2;
}
if(a[i]>3)
{
a[i]-=4;
dfs(x+1,depth+1,p-8);
a[i]+=4;
}
continue;
}
if(a[i]&&a[j])
{
--a[i];--a[j];
dfs(x+1,depth+1,p-6);
++a[i];++a[j];
}
if(a[i]>1&&a[j]>1&&i!=15&&j!=15)
{
a[i]-=2;a[j]-=2;
dfs(x+1,depth+1,p-8);
a[i]+=2;a[j]+=2;
}
}
a[x]+=4;
}
return;
}
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
T=read();n=read();
while(T--)
{
memset(a,0,sizeof(a));maxx=25;
for(int i=1;i<=n;++i)
{
int x,y;
x=read();y=read();
if(x==0&&y==1){++a[15];continue;}
if(x==0&&y==2){++a[15];continue;}
if(x==1){++a[14];continue;}
++a[x];
}
//for(int i=2;i<=15;++i)cout<<a[i]<<endl;
dfs(2,0,n);
put(maxx);
}
return 0;
}