人,只有自己站起来,这个世界才能属于他|

园龄:粉丝:关注:

题解:P2540 [NOIP2015 提高组] 斗地主 加强版

P2540 解题报告

前言

巨大抽象模拟搜索题。

写了一节自习课。

思路分析

首先因为一共就两副牌,可以直接搜索所有可能的情况。

然后就过了原题的数据了。

但是加强版数据有点强,这样写估计会 T 飞。

考虑发扬人类智慧。

如果你玩过斗地主的话,应该知道一些基本的策略。

比如说有顺子肯定不出单牌,四带二优先带单牌什么的。

总结一下我用的贪心策略:

  • 优先考虑顺子;

  • 多张数字相同的牌优先考虑在一起的情况,再考虑分开的情况;

  • 四带二时优先考虑单牌,然后考虑两对,最后考虑一对;

  • 三带一时优先考虑单牌,然后考虑一对。

所以实际上你可以先搜索所有顺子的情况,然后搜索所有的多张牌的情况,最后贪心地考虑散牌怎么出。

复杂度 O()

代码实现

其实前面想明白了并不难写。


#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int t,n,a,b,c[17],d[5],e[5],tot,ans,minn;
int getime(){
	memcpy(e,d,sizeof(e));
	int cnt=0;
	while(d[4]){
		if(d[1]>=2) d[1]-=2;
		else if(d[2]>=2) d[2]-=2;
		else if(d[2]>=1) d[2]--;
		else break;
		d[4]--;
		cnt++; 
	}
	while(d[3]){
		if(d[1]) d[1]--;
		else if(d[2]) d[2]--;
		else break;
		d[3]--;
		cnt++;
	}
	cnt+=d[1]+d[2]+d[3]+d[4];
	memcpy(d,e,sizeof(d));
	return cnt;
}
void dfs2(){
	tot=min(tot,getime());
	if(d[4]){
		d[4]--,d[3]++,d[1]++;
		dfs2();
		d[4]++,d[3]--,d[1]--;
		d[4]--,d[2]++,d[2]++;
		dfs2();
		d[4]++,d[2]--,d[2]--;
	}
	if(d[3]){
		d[3]--,d[2]++,d[1]++;
		dfs2();
		d[3]++,d[2]--,d[1]--;
	}
	if(d[2]){
		d[2]--,d[1]+=2;
		dfs2();
		d[2]++,d[1]-=2;
	}
}
void dfs1(int val){
	memset(d,0,sizeof(d));
	for(int i=2;i<=16;i++){
		d[c[i]]++;
	}
	tot=inf;
	dfs2();
	ans=min(ans,val+tot);
	for(int i=3;i<=14;i++){
		if(!c[i]) continue;
		for(int j=i+1;j<=14;j++){
			if(!c[j]) break;
			if(j-i+1<5) continue;
			for(int k=i;k<=j;k++){
				c[k]--;
			}
			dfs1(val+1);
			for(int k=i;k<=j;k++){
				c[k]++;
			} 
		}
	}
	for(int i=3;i<=14;i++){
		if(c[i]<2) continue;
		for(int j=i+1;j<=14;j++){
			if(c[j]<2) break;
			if(j-i+1<3) continue;
			for(int k=i;k<=j;k++){
				c[k]-=2;
			}
			dfs1(val+1);
			for(int k=i;k<=j;k++){
				c[k]+=2;
			} 
		} 
	} 
	for(int i=3;i<=14;i++){
		if(c[i]<3) continue;
		for(int j=i+1;j<=14;j++){
			if(c[j]<3) break;
			if(j-i+1<2) continue;
			for(int k=i;k<=j;k++){
				c[k]-=3;
			}
			dfs1(val+1);
			for(int k=i;k<=j;k++){
				c[k]+=3;
			}
		}
	}
} 
void solve(){
	minn=inf;
	int kin;
	kin=min(c[15],c[16]);
	c[15]-=kin;
	c[16]-=kin;
	ans=inf;
	dfs1(0);
	minn=min(minn,ans+kin);
	c[15]+=kin;
	c[16]+=kin;
	ans=inf;
	dfs1(0);
	minn=min(minn,ans);
	cout<<minn<<'\n';
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t>>n;
	while(t--){
		memset(c,0,sizeof(c));
		memset(d,0,sizeof(d));
		tot=ans=minn=inf;
		for(int i=1;i<=n;i++){
			cin>>a>>b;
			if(a==0 && b==1) c[15]++;
			if(a==0 && b==2) c[16]++;
			else if(a==1) c[14]++;
			else c[a]++;
		}
		solve();
	}
	return 0;
}

后记

在考前锻炼一下码力还是有必要的。

本文作者:Kenma

本文链接:https://www.cnblogs.com/Kenma/p/18689793

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _Kenma  阅读(8)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起