poj 1185炮兵阵地

/* Name: poj 1185炮兵阵地 Author: UnimenSun Date: 22/05/2011 15:06 Description: 状态压缩DP */ /* 解题报告: 算法: 1.利用二进制表示某个位置有无炮兵状态 2.把任意一行看成一个二进制串,并转化为相应的十进制数,即该行的压缩状态 3.枚举该行(i)所有二进制串,找出单独一行的所有可能的状态,即合法的压缩状态(s[i]) 4.利用滚动数组降低内存消耗 技巧: 二进制的位运算,滚动数组 动规方程: f[i][j][k] = max{f[i-1][k][l]+c[j]}, f[i][j][k]表示第i行状态为s[j],第i-1行状态为s[k]的最大炮兵数 枚举l的每种状态,且s[j],s[k],s[l],地形互不冲突 */ #include <iostream> #include <string> #include <cstring> using namespace std; int row, col; string szRow; int stamap[110]; int s[70], c[70], nNum; int dp[2][70][70]; //dp存储结果的数组 int roll; //实现滚动数组用的 int main() { int i, j, k, l; while(cin>>row>>col) { //读图并以行为基准压缩图放到stamap中 memset(stamap, 0, sizeof(stamap)); for(i=0; i<row; ++i) { cin>>szRow; for(j=0; j<col; ++j) { if(szRow[j] == 'H') stamap[i] += 1<<j; } } //在总的状态中,排除掉不可行的状态,以及这种状态下的可以放炮兵的个数 //这时排除的只是一行当中不能在相邻1个或2个时放炮兵,并没有考虑进山地,与上下行的情况 nNum = 0; for(i=0; i<(1<<col); ++i) { int temp = i; if( ((temp<<1)&i) || ((temp<<2)&i) ) continue; c[nNum] = temp%2; while( temp = (temp>>1) ) c[nNum] += temp%2; //i状态时有多少中放法,存入c中,其实就是统计i二进制中1的个数 s[nNum++] = i; //s中存放的是各种状态值 } //利用转移方程求解 roll = 0; memset(dp, 0, sizeof(dp)); for(i=0; i<row; ++i) { for(j=0; j<nNum; ++j) { if( s[j] & stamap[i] ) //状态j是否与i行的地形冲突 continue; if( 0 == i ) { dp[roll][j][0] = c[j]; } else if( 1 == i ) { for(k=0; k<nNum; ++k) { if( s[k] & stamap[i-1] ) continue; if( s[j] & s[k] ) continue; if( dp[roll][j][k] < dp[(roll+1)%2][k][0] + c[j] ) dp[roll][j][k] = dp[(roll+1)%2][k][0] + c[j]; } } else { for(k=0; k<nNum; ++k) { if( s[k] & stamap[i-1] ) continue; if( s[j] & s[k] ) continue; for( l=0; l<nNum; ++l) { if(s[l] & stamap[i-2]) continue; if(s[l] & s[k] || s[j]&s[l]) continue; if(dp[roll][j][k] < dp[(roll+1)%2][k][l] + c[j]) dp[roll][j][k] = dp[(roll+1)%2][k][l] + c[j]; } } } } roll = (roll + 1) % 2; //改变当行位置,便于下次循环求值 } //因为前面改变以一次当前行的位置,为的是便于循环因这还得变回来 roll = (roll + 1) % 2; int nMax = 0; for(j=0; j<nNum; ++j) { for (k=0; k<nNum; ++k) { if (nMax < dp[roll][j][k]) { nMax = dp[roll][j][k]; } } } cout<<nMax<<endl; } return 0; }

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