题解 HDU1565 【方格取数(1)】


给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。


题目清晰明了,这道题应该用dp。

自然地想到$dp[i][j]$表示位置$(i, j)$的最大值,但是在状态转移的时候推不出来。

这是因为子状态缺少表示选择情况的维度,即:你不知道某一个特定的点有没有被取。

因此自然地想到$dp[i][j][s]$其中$s$储存着一个状态。

但是经过简单的计算就可以发现,时间复杂度过高,即便这道题有5000ms的时限都妥妥的TLE。

因此我们需要状态压缩。

所以想到了$dp[i][s]$其中$i$表示第$i$行,而$s$表示这一行的选择状态,用一个二进制数储存。(0为留下1为取走)

思考一下不难得出状态转移方程:

 1 dp[i][s1] = max(dp[i][s1], dp[i - 1][s2] + tmp); 

其中$tmp$为第$i$行在$s1$状态下的值,$dp$数组初始化也是一样的。

要注意,题目中说明了格子不能相邻,所以在预处理的时候可以凭此缩小枚举范围。(不缩小会炸)

大致思路就是这样了,具体的一下优化因人而异。有一个要注意的点就是杭电最近会出玄学结果,刚开始本蒟蒻开小了数组导致越界,结果结果是WA,害得调了很久才发现。

完整AC代码如下:(好像是495ms吧)

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cmath>
 4 #include <algorithm>
 5 #include <cstring>
 6 #include <vector>
 7 using namespace std;
 8 
 9 inline int read() {
10     
11 }
12 
13 const int maxn = 21;
14 
15 int n;
16 int sq[maxn][maxn];
17 int top[maxn], ok[20000];
18 
19 void init() {
20     int tp = 0, co = 1;
21     for(int i = 0; i < (1 << (maxn - 1)); i++) {
22         if(!(i&(i<<1))) {
23             ok[tp++] = i;
24 //            cout << i << endl;
25         }
26         if(i == (1 << co) - 1) top[co++] = tp;
27     }
28 //    cout << top << endl;
29     return ;
30 } 
31 
32 int dp[maxn][20000];
33 
34 int value(int line, int state) {
35     int ans = 0, pos, t = 0;
36     while(state) {
37         pos = state % 2;
38         if(pos == 1) ans += sq[line][t];
39         t++;
40         state /= 2;
41     }
42     return ans;
43 }
44 
45 void dp_init() {
46 //    cout << top << endl;
47     for(int i = 0; i < n; i++) {
48         for(int s = 0; s < top[n]; s++) {
49             dp[i][s] = value(i, ok[s]);
50 //            cout << dp[i][s] << ' ';    
51         }
52 //        cout << endl;
53     }
54     return ;
55 }
56 
57 void solve() {
58     for(int i = 1; i < n; i++) {
59         for(int s1 = 0; s1 < top[n]; s1++) {
60             int tmp = dp[i][s1];
61             for(int s2 = 0; s2 < top[n]; s2++) {
62                 if(!((ok[s1] | ok[s1] << 1 | ok[s1] >> 1) & ok[s2])) {
63                     dp[i][s1] = max(dp[i][s1], dp[i - 1][s2] + tmp);
64                 }
65             }
66         }
67     }
68     return ;
69 } 
70 
71 int main() {
72     init();
73 //    for(int i = 1; i <= 20; i++) cout << top[i] << ' ';
74 //    cout << endl; 
75     while(scanf("%d", &n) != EOF) {
76         for(int i = 0; i < n; i++) {
77             for(int j = 0; j < n; j++) {
78                 scanf("%d", &sq[i][j]);
79             }
80         }
81         dp_init();
82         solve();
83         int ans = 0;
84         for(int i = 0; i < top[n]; i++) {
85             ans = max(ans, dp[n - 1][i]);
86         }
87         printf("%d
", ans);
88     }
89     return 0;
90 }

题外话:洛谷上好像有一道类似的紫题(网络流24题的),只不过不是正方形是长方形,而且实现也只有1000ms。

原文地址:https://www.cnblogs.com/ilverene/p/9819044.html