典型的状态压缩DP问题。第i行的取法只受到第i-1行的影响。首先每一行的取法要相容(不能有两个相邻),然后相邻行之间也要相容。将每一个格子看做两种状态,1表示取,0表示不取。这样每一行就是一个01串,恰好可以看做是一个二进制数,那么该二进制数对应的十进制整数可以唯一的表示为当前第 i 行的状态。定义用dp[i][j]表示前 i 行状态为j 时最大和。其中状态 j对应的整数为stu[j],数组stu[]收录的是所有合法的状态,所谓合法状态是:若一个整数的二进制中没有任意两个1相邻,那么该整数就是合法的状态。判定方法:若 x&(x<<1)==0,则x是合法的状态。状态转移方程如下:
dp[i][j]=max{dp[i-1][w]}+value[i][j] (w&j==0) ;
方程含义:j是第i行状态,w是第i-1行的状态,value[i][j]是第i行状态为j时第i行取法的和。从w状态转移到j状态需要满足w&j==0(他们是相容的)。计算时候需要枚举第i行所有状态w。代码如下:
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> using namespace std; #define MAX_SIZE 17712 //当n=20时可以计算得到最多有11710中状态 int stu[MAX_SIZE], Value[MAX_SIZE]; //stu表示总的状态数,MAX_SIZE表示对应状态的值 int a[22][22], dp[22][MAX_SIZE]; int initialization(int n); int stuValue(int i, int j); int main(){ int i, j, k, m, n, lmax, maxSum; while (~scanf("%d", &n)){ m = initialization(n); for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) scanf("%d", &a[i][j]); memset(dp[0], 0, sizeof(dp[0])); maxSum = 0; for (i = 1; i <= n; i++) for (j = 0; j < m; j++){ lmax = 0; for (k = 0; k < m; k++){ if (!(stu[j] & stu[k]) && lmax < dp[i - 1][k]) //如果上下相容,且当前的值大于原来最大值 lmax = dp[i - 1][k]; } dp[i][j] = lmax + stuValue(i, j); //计算状态dp[i][j] if (maxSum < dp[i][j]) maxSum = dp[i][j]; } printf("%d\n", maxSum); } return 0; } int initialization(int n){ int ant = 0; for (int i = 0; i < (1 << n); i++){ if (!(i&(i << 1))) stu[ant++] = i; } return ant; } int stuValue(int i, int j){ //第i行,状态为stu[j]时候第i行的值 int k = stu[j], t = 1, sum = 0; while (k>0){ if (k & 1) sum += a[i][t]; t++; k = k >> 1; } return sum; }
时间: 2024-10-10 16:22:09