【NOIP2015提高组Day1】斗地主

在这里插入图片描述
嘿嘿,学老贺乱搞

题面

在这里插入图片描述
博弈论???
博弈论???
贪心???
一脸懵逼*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掉
于是

  1. 大王、小王可以当普通牌,被三带,四带。
  2. DFS顺序是先枚举顺子,再枚举四带、三带。
  3. 枚举四带、三带的时候不带任何牌,等最后再带剩下的单张和对子。(这个很关键,因为如果你枚举了要带哪些牌,每次都成为一种情况,时间就被拖得很慢;但事实上既然带哪些其实都可以,那肯定是带哪些“没用”的,也就是搜完其它后余下的单张或对子。)
  4. 最后当发现没有特殊出牌方法的时候,再处理单张和对子。
  5. 我们考虑记录下你枚举的四带和三带的个数,最后剩下的单张和对子想办法塞进去,让四带和三带尽可能多地带上它们,如果最后实在没办法带上了,就只能一对一对、一张一张地出了。
  6. 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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值