poj 1185 炮兵阵地(状压dp)

题目链接:http://poj.org/problem?id=1185

状压dp跑了375ms...

状态转移方程是参考网上的思路...

dp[i][j][k]表示第i行状态为j,第i-1行状态为k的最优解

开一个三维数组,而大小为2*1<<15*1<<15,肯定会MLE

如果筛选出同一行内的可行状态,其实最多只有60种(可以取m最大输出stateNum即可)

状态转移方程为dp[r%2][i][j]=max(dp[r%2][i][j],dp[(r-1)%2][j][k]+num[i]);(滚动数组优化空间)

初始化:

num[i]表示状态i中1的个数,即炮车的数量

则dp[1][i][1]=num[i];

然后枚举就可以了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;
const int maxn=105;
int T,n,m;
int map[maxn],state[maxn],num[maxn];
int dp[2][maxn][maxn];//dp[i][j][k]表示第i行状态为j,第i-1行状态为k的最优解
int stateNum;

inline bool ok1(int x){//判断x状态是否可行
	return !(x&(x<<1))&&!(x&(x<<2));
}

inline bool ok2(int x,int y){//判断x状态与第y行状态是否可行
	return !(x&map[y]);
}

inline int getNum(int x){
	int ans=0;
	while(x){
		ans+=x%2;
		x>>=1;
	}
	return ans;
}

void init(){
	memset(dp,-1,sizeof(dp));
	memset(map,0,sizeof(map));
	stateNum=0;
	int cnt=(1<<m);
	for(int i=0;i<cnt;i++){//筛选具有满足横向的可行状态
		if(ok1(i)){
			num[++stateNum]=getNum(i);
			state[stateNum]=i;
		}
	}
}

int main(){
	while(~scanf("%d%d",&n,&m)){
		init();
		char s[maxn];
		for(int i=1;i<=n;i++){
			scanf("%s",s);
			for(int j=0;j<m;j++){
				if(s[j]=='H')
					map[i]|=(1<<j);
			}
		}
		for(int r=1;r<=n;r++){
			for(int i=1;i<=stateNum;i++){//枚举第i行的状态
				if(!ok2(state[i],r)) continue;
				if(r==1){
					dp[1][i][1]=num[i];
				}
				else{
					for(int j=1;j<=stateNum;j++){//枚举第i-1行的状态
						if(!ok2(state[j],r-1)) continue;
						if(state[i]&state[j]) continue;
						for(int k=1;k<=stateNum;k++){//枚举第i-2行的状态
							if(state[i]&state[k]) continue;
							if(dp[(r-1)%2][j][k]==-1) continue;
							dp[r%2][i][j]=max(dp[r%2][i][j],dp[(r-1)%2][j][k]+num[i]);
						}
					}
				}
			}
		}
		int ans=0;
		for(int i=1;i<=stateNum;i++){
			for(int j=1;j<=stateNum;j++){
				ans=max(ans,dp[n%2][i][j]);
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}


你可能感兴趣的:(poj 1185 炮兵阵地(状压dp))