[PKUSC2018]主斗地
暴搜
非常暴力的搜索,以至于我都不相信我能过。
方法是:暴力枚举所有牌型,然后暴力判断是否可行。
暴力枚举部分:
非常暴力:
void dfs(int x,int l){
if(l==0){
flag=0;
check(1,0,0,0,0);
if(flag)ans++;
return;
}
if(x>14)return;
for(res i=0;i<=limit[x]&&i<=l;++i){
jiry[x]=i;
dfs(x+1,l-i);
}
}
搜下来发现牌型数量不大于\(3000000\),因为出题人良心的把3都去掉了,所以总牌型大量缩减。
判断部分:
这里需要用到一个性质,如果一幅飞机或顺子可以大于另外一幅同类型的牌,那么把他拆开来也可以大于另外一幅牌拆开来。
判断部分分两部分,先暴力搜索搜出哪些牌要被拆出来变成三张牌或四张牌。
因为双方的总牌数都不会很大,所以直接搜只会带一个常数。
这里要注意,只有当九条可怜出了三张牌或四张牌以后,xx网友才能出三张牌或四张牌,不然九条可怜将无法出小于xx网友的牌型。
最后两者出的三张牌和四张牌数量必须相等,不然会有一副牌无法有对应的牌。
void check(int x,int one,int oneortwo,int three,int four){
if(x>14){
if(three||four)return;
check(one,oneortwo);
return;
}
if(jiry[x]>=4){
jiry[x]-=4;
check(x+1,one+1,oneortwo,three,four+1);
jiry[x]+=4;
if(flag)return;
}
if(jiry[x]>=3){
jiry[x]-=3;
check(x+1,one,oneortwo+1,three+1,four);
jiry[x]+=3;
if(flag)return;
}
if(xx[x]>=4&&four){
xx[x]-=4;
check(x+1,one,oneortwo,three,four-1);
xx[x]+=4;
if(flag)return;
}
if(xx[x]>=3&&three){
xx[x]-=3;
check(x+1,one,oneortwo,three-1,four);
xx[x]+=3;
if(flag)return;
}
check(x+1,one,oneortwo,three,four);
}
然后再用贪心的方式判断最后的散牌是否可以。
枚举有多少的三张牌要带一个,然后得到有多少的一张牌和多少的两张牌可以被带掉。
对于九条可怜的牌,去掉最大的牌。对于xx网友的牌,去掉最小的牌。要先用两张牌来贪心,再用一张牌来贪心。
剩下的牌直接扫一遍,如果在前\(i\)种牌中xx网友的牌比九条可怜的要多,则当前牌错误。
void check(int one,int oneortwo){
for(res i=0;i<=oneortwo;i++){
memcpy(jirycpy,jiry,sizeof jiry);
memcpy(xxcpy,xx,sizeof xx);
res o=i+one*2,t=oneortwo-i;
for(res j=1;j<=14;++j){
while(xxcpy[j]>=2&&t)xxcpy[j]-=2,t--;
while(xxcpy[j]>=1&&o)xxcpy[j]-=1,o--;
}
if(o||t)continue;
o=i+one,t=oneortwo-i;
for(res j=14;j;--j){
while(jirycpy[j]>=2&&t)jirycpy[j]-=2,t--;
while(jirycpy[j]>=1&&o)jirycpy[j]-=1,o--;
}
if(o||t)continue;
flag=1;
for(res j=1,now=0;j<=14;++j){
now-=xxcpy[j];
if(now<0){
flag=0;
break;
}
now+=jirycpy[j];
}
if(flag)return;
}
}
然后是完整代码
#include<bits/stdc++.h>
#define res register int
using namespace std;
char str[15];
int jiry[15],xx[15],limit[15];
int jirycpy[15],xxcpy[15];
int cl(char c){
if(c=='T')return 7;
else if(c=='J')return 8;
else if(c=='Q')return 9;
else if(c=='K')return 10;
else if(c=='A')return 11;
else if(c=='2')return 12;
else if(c=='w')return 13;
else if(c=='W')return 14;
else return c-'3';
}
bool flag;
int ans;
void check(int one,int oneortwo){
for(res i=0;i<=oneortwo;i++){
memcpy(jirycpy,jiry,sizeof jiry);
memcpy(xxcpy,xx,sizeof xx);
res o=i+one*2,t=oneortwo-i;
for(res j=1;j<=14;++j){
while(xxcpy[j]>=2&&t)xxcpy[j]-=2,t--;
while(xxcpy[j]>=1&&o)xxcpy[j]-=1,o--;
}
if(o||t)continue;
o=i+one,t=oneortwo-i;
for(res j=14;j;--j){
while(jirycpy[j]>=2&&t)jirycpy[j]-=2,t--;
while(jirycpy[j]>=1&&o)jirycpy[j]-=1,o--;
}
if(o||t)continue;
flag=1;
for(res j=1,now=0;j<=14;++j){
now-=xxcpy[j];
if(now<0){
flag=0;
break;
}
now+=jirycpy[j];
}
if(flag)return;
}
}
void check(int x,int one,int oneortwo,int three,int four){
if(x>14){
if(three||four)return;
check(one,oneortwo);
return;
}
if(jiry[x]>=4){
jiry[x]-=4;
check(x+1,one+1,oneortwo,three,four+1);
jiry[x]+=4;
if(flag)return;
}
if(jiry[x]>=3){
jiry[x]-=3;
check(x+1,one,oneortwo+1,three+1,four);
jiry[x]+=3;
if(flag)return;
}
if(xx[x]>=4&&four){
xx[x]-=4;
check(x+1,one,oneortwo,three,four-1);
xx[x]+=4;
if(flag)return;
}
if(xx[x]>=3&&three){
xx[x]-=3;
check(x+1,one,oneortwo,three-1,four);
xx[x]+=3;
if(flag)return;
}
check(x+1,one,oneortwo,three,four);
};
void dfs(int x,int l){
if(l==0){
flag=0;
check(1,0,0,0,0);
if(flag)ans++;
return;
}
if(x>14)return;
for(res i=0;i<=limit[x]&&i<=l;++i){
jiry[x]=i;
dfs(x+1,l-i);
}
}
int main(){
scanf("%s",str+1);
int len=strlen(str+1);
for(int i=1;i<=12;++i){
limit[i]=4;
}
limit[13]=limit[14]=1;
memset(xx,0,sizeof xx);
for(int i=1;i<=len;++i){
limit[cl(str[i])]--;
xx[cl(str[i])]++;
}
ans=0;
dfs(1,17);
printf("%d\n",ans);
}