hdoj 4272 LianLianKan 数据太水

点击打开链接

题意:

给出一个序列,其中距离不超过6的两个相同的数字可以消除掉(从上往下消,输入是从底向上的),问能不能全部消除。


思路:

状压dp http://www.cnblogs.com/swm8023/archive/2012/09/10/2679455.html

因为最坏情况下,它后面的四个数字能被它前面的四个数字消掉,这样它就能和原来是它后面的第9个元素相消了,最多10个状态


状态转移:

如果st的第1说明这一位位为0,已经被消掉,d[i][st]=dp(i+1,next(st))。

如果第1为为1,向后连续找至多五个为1的位,比较是否和第一位数字相同,如果相同,就将st的这两位置为0,然后

d[i][st]=d(i+1,next(newst)),newst=st&~1&~(1<<k),其中x[k]==x[i]。

next(st)这个函数是求将st传递到下一个位置时的状态,如果(n-(p+1) > 9) st=1<<9|st>>1,否则st=st>>1,因为只有当后面数字多于10个时,才会有新的数字加入到状态中。

代码一:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mem(a) memset(a,0,sizeof(a))
#define mp(x,y) make_pair(x,y)
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//
const int maxn = 1e3+10;

int n;
ll a[maxn];
int dp[maxn][1<<10],full;

int nx(int p,int st){
	if(n-(p+1) > 9) return (1<<9) | (st>>1);
	else return st>>1;
}

int dfs(int p,int st){
	if(p == n) return st==0;
	if(dp[p][st] != -1) return dp[p][st];
	dp[p][st] = 0; // 二维 表示在p这个位置[保证可以记忆化,不被覆盖] 从p开始的10个数字的状态为st时是否可消

	if((st&1) == 0) dp[p][st] = dfs(p+1,nx(p,st));
	else{
		int cnt = 0;
		for(int i=1; i<10; i++){
			if((1<<i & st) == (1<<i)){
			 // if (1<<i&st){
			 	// cout << "aa " << i << " " << st << " " << (1<<i&st) << endl;
				cnt++;
				if(cnt > 5) break;
				if(a[p] == a[p+i]){
					int newst = st & ~(1<<i) & ~1;
					if(dfs(p+1,nx(p,newst))){
						dp[p][st] = 1;
						break;
					}
				}
			}
		}
	}
	return dp[p][st];
}

int main(){	
	while(scanf("%d",&n) != EOF){
		memset(dp,-1,sizeof(dp));
		for(int i=1; i<=n; i++)
			a[n-i] = read();
		full = (1<<min(n,10)) - 1;

		cout << dfs(0,full) << endl;		
	}

    return 0;
}

代码二: 直接dfs 模拟

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mem(a) memset(a,0,sizeof(a))
#define mp(x,y) make_pair(x,y)
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
//
const int maxn = 1e3+10;
map<int,int> mp;
map<int,int>::iterator it;
int a[maxn],used[maxn];

int dfs(int n){
	while(n>0 && used[n]) n--;
	if(n==0) return 1;
	if(n==1) return 0;
	int i = 0;	
	int j = n-1;
	while(i<=5){
		if(j<=0) return 0;
		if(used[j]) {
			j--;
			continue;
		}
		if(a[n] == a[j]){
			used[j] = 1;
			if(dfs(n-1)) return 1;
			used[j] = 0;
		}
		i++;
		j--;
	}
	return 0;
}

int main(){
	int n;
	while(scanf("%d",&n)!=EOF){
		mp.clear();
		mem(used);
		for(int i=1; i<=n; i++){
			a[i] = read();
			mp[a[i]]++;
		}

		int flag = 1;
		for(it=mp.begin(); it!=mp.end(); it++){
			if(it->second % 2){
				flag = 0;
				break;
			}
		}
		if(!flag){
			cout << 0 << endl;
			continue;
		}
		// cout << 1 << endl;
		cout << dfs(n) << endl;
	}


    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值