【NOIP2015提高组Day1】斗地主

博客介绍了NOIP2015提高组的斗地主题目,包括题目的描述、输入输出示例以及数据约束。博主分享了使用DFS解决该问题的思路,并提到存在BFS+hash的高效解决方案。代码部分展示了两种DFS方法,其中一种能通过所有测试用例,避免被hack。
摘要由CSDN通过智能技术生成

题面

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

输入1:
1 8
7 4
8 4
9 1
10 4
11 1
5 1
1 4
1 1
输入2:
1 17
12 3
4 3
2 3
5 4
10 2
3 3
12 2
0 1
1 3
10 1
6 2
12 1
11 3
5 2
12 4
2 2
7 2

Sample Output

输出1:
3
在这里插入图片描述

输出2:
6

Data Constraint

在这里插入图片描述

思路

作为Day1的第三题还是有点难的,废话,不难怎么能做第三题呢?
不皮了,这题我的做法是 D F S DFS DFS,据说某些dalao用 B F S + h a s h BFS+hash BFS+hash过了这道题。其实我的做法有点小问题,后面还会说到。


DFS的做法:
每次进入递归程序先把能做的三带,四带做完,把剩下的对子、单张、三张和炸弹打出去,步数与 a n s ans ans m i n min min值。接着把单顺,双顺,三顺都模拟一遍,答案就出来了。

Code

#include<cstdio>//by hzq
#include<cstring>
#define clean(x) memset(x,0,sizeof(x))
#define inf 99999999
using namespace std;
int t,n,ans,x,y;
int sum,jl[30];
int c[30];
int min(int mn1,int mn2){if(mn1>mn2) return mn2;return mn1;}
void dg(int step)
{
	if(step>=ans) return ;
	sum=0,clean(jl);
	for(int i=0;i<=13;++i) jl[c[i]]++;
	while(jl[4]>0&&jl[2]>1) jl[4]--,jl[2]-=2,sum++;
	while(jl[4]>0&&jl[1]>1) jl[4]--,jl[1]-=2,sum++;
	while(jl[4]>0&&jl[2]>0) jl[4]--,jl[2]--,sum++;
	while(jl[3]>0&&jl[2]>0) jl[3]--,jl[2]--,sum++;
	while(jl[3]>0&&jl[1]>0) jl[3]--,jl[1]--,sum++;
	ans=min(ans,step+sum+jl[1]+jl[2]+jl[3]+jl[4]);
	for(int i=2;i<=13;++i)
	{
		int cnt=i;
		while(c[cnt]>=3) cnt++;
		if(cnt-i>=2)
		{
			for(int j=i+1;j<=cnt-1;++j)
			{
				for(int p=i;p<=j;++p) c[p]-=3;
				dg(step+1);
				for(int p=i;p<=j;++p) c[p]+=3;
			}
		}
	}
	for(int i=2;i<=11;++i)
	{
		int cnt=i;
		while(c[cnt]>=2) cnt++;
		if(cnt-i>=3)
		{
			for(int j=i+2;j<=cnt-1;++j)
			{
				for(int p=i;p<=j;++p) c[p]-=2;
				dg(step+1);
				for(int p=i;p<=j;++p) c[p]+=2;
			}
		}
	}
	for(int i=2;i<=10;++i)
	{
		int cnt=i;
		while(c[cnt]>=1) cnt++;
		if(cnt-i>=5)
		{
			for(int j=i+4;j<=cnt-1;++j)
			{
				for(int p=i;p<=j;++p) c[p]--;
				dg(step+1);
				for(int p=i;p<=j;++p) c[p]++;
			}
		}
	}
}
int main()
{
	freopen("landlords.in","r",stdin);
	freopen("landlords.out","w",stdout);
	scanf("%d%d",&t,&n);
	for(int i=1;i<=t;++i)
	{
		ans=inf,clean(c);
		for(int j=1;j<=n;++j)
		{
			scanf("%d%d",&x,&y);
			if(x==1) x=13;
			else if(x) x--;
			c[x]++;
		}
		dg(0),printf("%d\n",ans);
	}
	return 0;
}

这里还有另外一种 D F S DFS DFS的方法

#include<bits/stdc++.h>//by zhy
#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 ;
	}
	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;
}

还有比赛的时候一次就AC的 B F S + h a s h BFS+hash BFS+hash

#include<iostream>//by pmx
#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;
}

小彩蛋

还好这题是随机数据,不然一堆人都会被hack掉,试一下下面这个数据吧:
输入:
1 8
3 1
3 2
3 3
3 4
4 1
4 2
4 3
4 4
输出:
1
解释:
四个三带两对四。


我给出的第二个代码是不会被hack掉的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值