题目链接:http://poj.org/problem?id=1185
题意:汉语
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
int map[110];
int N, M;
int dp[2][65][65];//滚动数组
int state[65];
int soilder[65];
/*
状态转移方程:
dp[i][j][k] = max{ dp[i][j][k], d[i-1][k][p] + soilder[j] };
dp[i][j][k]的含义:前i行在第i行对应的第j种状态,第i-1行对应的第k种状态的情况下,总的炮兵数目。
总的状态数:M=10时,总的状态数为60.(运行get_all_state()函数即可知道)。
总的状态数的求法:在只考虑一行中互不攻击的情况下(不考虑山地的情况),一行中可以放置的炮兵的状态数。
一种状态,对应着这种状态下炮兵的数目,也对应着这种状态下一行中炮兵的分布情况。
举个例子:假设M=4.(平原为0,山地为1).一行中的输入是PPHP,即0010.
先求总的状态数:在不考虑山地的情况下,初始情况为0000.你可以在任意位置放置1,但是需要满足互不攻击的条件。
因为4位2进制数可表示32个整数,枚举这32个整数,看哪个整数满足互不攻击条件。若满足,即将这个整数记下来,并且求出这个
整数的所有的1.(即放置炮兵的数目)。
以上就是状态压缩:把一行输入看成一组2进制数,并转换成10进制存储。
其实这题最难想的还是状态转移方程。
*/
//只考虑互不攻击,未考虑山地的情况
//当M=10,所有的状态数为60
int get_all_state() {
int i, num;
for(i=0, num=0; i<(1<<M); i++) {
int k = i;
if(((k<<1)&i) || ((k<<2)&i)) continue;
state[num] = i;
while(k) {
soilder[num]++;
k=k&(k-1);
}
num++;
}
return num;
}
int main()
{
freopen("/home/lsy/Desktop/2.txt", "r", stdin);
cin >> N >> M;
int i, j, k, p;
char ch;
for(i=0; i<N; i++) {
map[i] = 0;
for(j=0; j<M; j++) {
cin >> ch;
if(ch == 'H') k=1;
else k=0;
map[i] = map[i]*2+k;
}
}
int kind = get_all_state();
//cout << "kind=" << kind << endl;
memset(dp, -1, sizeof(dp));
for(i=0; i<kind; i++) {
if((state[i]&map[0]) == 0) dp[0][i][0] = soilder[i];
}
for(i=1; i<N; i++) {
for(j=0; j<kind; j++) {
for(k=0; k<kind; k++) {
for(p=0; p<kind; p++) {
if(state[j]&map[i]) continue;
if(state[j]&state[k] || state[j]&state[p] || state[k]&state[p]) continue;
if(dp[(i-1)%2][k][p] != -1)
dp[i%2][j][k] = max(dp[i%2][j][k], dp[(i-1)%2][k][p] + soilder[j]);
}
}
}
}
int ans = 0;
for(k=0; k<kind; k++)
for(j=0; j<kind; j++) {
if(ans < dp[(i-1)%2][k][j]) ans = dp[(i-1)%2][k][j];
}
cout << ans << endl;
return 0;
}
总结:状态的表示和状态转移方程比较难想。看来做动态规划的题目,先得表示所有状态,再写出状态转移方程。