首页 > 代码库 > HDU 4539 郑厂长系列故事——排兵布阵 (状态压缩DP)

HDU 4539 郑厂长系列故事——排兵布阵 (状态压缩DP)



中文题,题意不再累赘。


思路:对于第 i 行的放士兵,影响它的只有第 i-1 行和 i-2 行,所以暴力枚举符合这三行的状态


 state[i],state[j],state[k].  接下来就是二进制的巧妙应用了。


具体题解看代码注释!!!


#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<map>
#include<cmath>
#include<iostream>
#include <queue>
#include <stack>
#include<algorithm>
#include<set>
using namespace std;
#define INF 1e8
#define inf -0x3f3f3f3f
#define eps 1e-8
#define LL long long
#define N 100001
#define mol 100000000

int lp(int a,int b)
{
	return a&b;
}
int dp[105][200][200];//dp[i][j][k],表示第 i 行是 state[j]状态i-1行是state[k]状态的最大值
int state[200];//符合规定的状态
int base[200];//给定矩阵的初始状态
int sum[200];//sum[i]:状态satet[i]的士兵数
int n,m,g;
int main()
{
	while(~scanf("%d%d",&n,&m))
	{
		memset(base,0,sizeof(base));  
        memset(dp,0,sizeof(dp));  
        memset(state,0,sizeof(state));  
        memset(sum,0,sizeof(sum));  
		if(n==0||m==0)
		{
			printf("0\n");
			continue;
		}
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)
			{
				scanf("%d",&g);
				if(g==0)
					base[i]+=1<<(j);//初始状态二进制表示
			}
		}
		int num=0;
		for(int i=0;i<(1<<m);i++)
		{
			if(lp(i,i<<2)||lp(i,i>>2)) continue;//状态i要满足它不能攻击它的前两个和后两个
			int k=i;
			while(k)//算出k有多少个1(士兵)
			{
				sum[num]+=k&1;
				k=k>>1;
			}
			state[num++]=i;
		}
		for(int i=0;i<num;i++)//初始第0行的状态
		{
			if(lp(state[i],base[0])) continue;
			dp[0][i][0]=sum[i];
		}
		for(int i=0;i<num;i++)//在满足第0行的状态下寻找第1行的状态
		{
			if(lp(state[i],base[1])) continue;
			for(int j=0;j<num;j++)
			{
				if(lp(state[j],base[0])) continue;
				if(lp(state[j]<<1,state[i])||lp(state[j]>>1,state[i])) continue;
				dp[1][i][j]=max(dp[1][i][j],dp[0][j][0]+sum[i]);
			}
		}
		for(int i=0;i<num;i++)//在满足第0,1行的状态下寻找第2行的状态
		{
			if(lp(state[i],base[2])) continue;
			for(int j=0;j<num;j++)
			{
				if(lp(state[j],base[1])) continue;
				for(int k=0;k<num;k++)
				{
					if(lp(state[k],base[0])) continue;
					if(lp(state[i]<<1,state[j])||lp(state[i]>>1,state[j])) continue;
					if(lp(state[j]<<1,state[k])||lp(state[j]>>1,state[k])) continue;
					if(lp(state[i],state[k])) continue;
					dp[2][i][j]=max(dp[2][i][j],dp[1][j][k]+sum[i]);
				}
			}
		}
		for(int r=3;r<n;r++)//从第3行开始
		{
			for(int i=0;i<num;i++)//暴力枚举三行的状态state[i],state[j],state[k]
			{
				if(lp(base[r],state[i])) continue;
				for(int j=0;j<num;j++)
				{
					if(lp(base[r-1],state[j])) continue;
					if(lp(state[i]<<1,state[j])||lp(state[i]>>1,state[j])) continue;
					for(int k=0;k<num;k++)
					{
						if(lp(base[r-2],state[k])) continue;
						if(lp(state[j]<<1,state[k])||lp(state[j]>>1,state[k])) continue;
						if(lp(state[i],state[k])) continue;
						dp[r][i][j]=max(dp[r-1][j][k]+sum[i],dp[r][i][j]);
					}
				}
			}
		}
		int ans=0;
		for(int i=0;i<num;i++)
		{
			for(int j=0;j<num;j++)
				ans=max(ans,dp[n-1][i][j]);
		}
		printf("%d\n",ans);
	}
	return 0;
}
/*
6 6
1 0 0 0 0 1
0 0 0 0 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 1 1 0 0
1 0 1 1 0 1

3 3
1 1 1
1 1 1
1 1 1

6 6
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0

6 6
1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1

0 6

1 6
1 1 1 1 1 1

2 6
1 1 1 1 1 1
1 1 1 1 1 1

Answer:
6
3
2
12
0
4
4
*/