嘿嘿,学老贺乱搞
题面
博弈论???
贪心???
一脸懵逼*1???
一脸懵逼*2???
一脸懵逼*3???
??????????
心路历程
如上…
题解
话不多说,正解,上
官方版本
顺子最特殊, 我们可以先搜出每一种顺子一共有几个.
之后再搜四带二, 我们可以枚举每个四张牌是带了两张单牌还是两对, 然后再枚举它带了哪
些.
之后我们可以发现, 三带一的情况是可以直接贪心的. 因为如果你有三张牌, 你肯定会让它
带另外一张牌或者另外一对. 所以此时我们直接算就可以了.
3 斗地主 3
我们可以加一个非常简单的全局的最优化剪枝, 并且很显然, 一开始可以令这个最优值为我
们把所有牌分开打出去所需要的步数.
代码
无
彭奆版本
bfs+哈希???
#include<iostream>
#include<cstdio>
using namespace std;
int hash[1000001][2];
int T,n;
int k[500001][15];
int prime[21]={13,131,373,499,641,773,929,1051,1181,1297,1439,1553,1667,1801};
int head,tail,bz;
int check(int x)
{
int w=x%999991;
while(hash[w][0]==T&&(hash[w][1]!=0&&hash[w][1]!=x))
{
w++;
if(w>1000000)
w=0;
}
if(hash[w][0]==T&&hash[w][1]==x) return 1;
hash[w][0]=T;
hash[w][1]=x;
return 0;
}
void add()
{
tail++;
int hash_num=0,bbz=0;
k[tail][14]=k[head][14]+1;
for(int i=0;i<=13;i++)
{
k[tail][i]=k[head][i];
if(k[tail][i]>0)
bbz=1;
hash_num+=k[head][i]*prime[i];
}
if(bbz==0)
{
bz=1;
printf("%d\n",k[tail][14]);
}
if(check(hash_num)==1)
tail--;
return ;
}
void a_kind(int x,int num)
{
if(k[head][x]<num) return ;
k[head][x]-=num;
add();if(bz==1) return ;
k[head][x]+=num;
return ;
}
void two_kind(int x,int num1,int num2)
{
if(k[head][x]<num1) return ;
k[head][x]-=num1;
for(int i=0;i<=13;i++)
if(i!=x&&k[head][i]>=num2)
{
k[head][i]-=num2;
add();if(bz==1) return ;
k[head][i]+=num2;
}
k[head][x]+=num1;
return ;
}
void three_kind(int x,int num1,int num2,int num3)
{
if(k[head][x]<num1) return ;
k[head][x]-=num1;
for(int i=0;i<=13;i++)
if(i!=x&&k[head][i]>=num2)
{
k[head][i]-=num2;
for(int j=0;j<=13;j++)
if(j!=x&&j!=i&&k[head][j]>=num3)
{
k[head][j]-=num3;
add();if(bz==1) return ;
k[head][j]+=num3;
}
k[head][i]+=num2;
}
k[head][x]+=num1;
return ;
}
void lian(int x,int num,int len)
{
if(x==0||x==2) return ;
int qi=x,i=0;
while(x!=2)
{
if(k[head][x]>=num)
{
++i;
k[head][x]-=num;
if(i>=len)
{
add();if(bz==1) return ;
}
x++;
if(x>13) x=1;
}
else break;
}
while(qi!=x)
{
k[head][qi]+=num;
qi++;
if(qi>13) qi=1;
}
}
int main()
{
freopen("landlords.in","r",stdin);
freopen("landlords.out","w",stdout);
scanf("%d%d",&T,&n);
while(T>0)
{
head=1,tail=1;
for(int i=0;i<=14;i++)
k[head][i]=0;
for(int i=1;i<=n;i++)
{
int x,b;
scanf("%d%d",&x,&b);
k[head][x]++;
}
bz=0;
while(head<=tail)
{
for(int i=0;i<=13;i++)
if(k[head][i]>0)
{
a_kind(i,1);if(bz==1) break;
a_kind(i,2);if(bz==1) break;
a_kind(i,3);if(bz==1) break;
a_kind(i,4);if(bz==1) break;
two_kind(i,3,1);if(bz==1) break;
two_kind(i,3,2);if(bz==1) break;
two_kind(i,4,2);if(bz==1) break;
three_kind(i,4,1,1);if(bz==1) break;
three_kind(i,4,2,2);if(bz==1) break;
lian(i,1,5);if(bz==1) break;
lian(i,2,3);if(bz==1) break;
lian(i,3,2);if(bz==1) break;
}
if(bz==1)
break;
head++;
}
T--;
}
fclose(stdin);fclose(stdout);
return 0;
}
本人解法
其实上面的官方解法有问题(嘿嘿嘿)
如4个3,4个4,就能hack掉
于是
- 大王、小王可以当普通牌,被三带,四带。
- DFS顺序是先枚举顺子,再枚举四带、三带。
- 枚举四带、三带的时候不带任何牌,等最后再带剩下的单张和对子。(这个很关键,因为如果你枚举了要带哪些牌,每次都成为一种情况,时间就被拖得很慢;但事实上既然带哪些其实都可以,那肯定是带哪些“没用”的,也就是搜完其它后余下的单张或对子。)
- 最后当发现没有特殊出牌方法的时候,再处理单张和对子。
- 我们考虑记录下你枚举的四带和三带的个数,最后剩下的单张和对子想办法塞进去,让四带和三带尽可能多地带上它们,如果最后实在没办法带上了,就只能一对一对、一张一张地出了。
- by GMOJ题解
代码
#include<bits/stdc++.h>
#pragma GCC optimize(3,"Ofast","inline")
#define rg register
#define Fu(i,a,b) for(rg int i=(a);i<=(b);i++)
#define Fd(i,a,b) for(rg int i=(a);i>=(b);i--)
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
inline int read(){ int f=1,x=0; char s=getchar(); while(s<'0'||s>'9') { if (s=='-') f=-1; s=getchar(); } while(s>='0'&&s<='9') { x=x*10+s-'0'; s=getchar(); } return x*f; }
using namespace std;
int t,n,N,a,v[20],b,ans,j,y;
void dfs(int o,int num){
if(n==0){
ans=min(ans,o);
return ;
}
if(ans<=o) return ;
int s1=0,s2=0,s3=0,bj=0,sum=0;
Fu(i,1,13) sum+=(v[i]+1)/2;
if(sum<=num){
if(ans>o) ans=o,y=num-sum;
return ;
}
// cout<<sum<<endl;
Fu(i,1,13){
if(v[i]>=1) s1++;
else s1=0;
if(v[i]>=2) s2++;
else s2=0;
if(v[i]>=3) s3++;
else s3=0;
if(i!=13){
if(s1>=5){
Fd(j,i,i-s1+1) v[j]--;
n-=s1;
dfs(o+1,num);
Fd(j,i,i-s1+1) v[j]++;
n+=s1;
bj=1;
}
if(s2>=3){
Fd(j,i,i-s2+1) v[j]-=2;
n-=s2*2;
dfs(o+1,num);
Fd(j,i,i-s2+1) v[j]+=2;
n+=s2*2;
bj=1;
}
if(s3>=2){
Fd(j,i,i-s3+1) v[j]-=3;
n-=s3*3;
dfs(o+1,num);
Fd(j,i,i-s3+1) v[j]+=3;
n+=s3*3;
bj=1;
}
}
if(v[i]>=3){
v[i]-=3,n-=3;
dfs(o+1,num+1);
v[i]+=3,n+=3;
bj=1;
}
if(v[i]==4){
v[i]-=4,n-=4;
dfs(o+1,num+2);
v[i]+=4,n+=4;
bj=1;
}
}
if(bj==0){
ans=min(ans,o+sum-num);
return ;
}
}
int main(){
fre(landlords);
t=read(),N=read();
while(t--){
n=N;
memset(v,0,sizeof(v));
ans=N,j=0,y=0;
Fu(i,1,n){
a=read(),b=read();
if(a==0)j++;
else{
if(a>2)a-=2;
else{
a+=11;
}
v[a]++;
}
}
n-=j;
dfs(0,0);
if(j>0&&y==0)ans++;
printf("%d\n",ans);
}
return 0;
}