• 题解 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。

  • 相关阅读:
    dubbo+zookeeper报错:com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method
    CoreException: Could not get the value for parameter compilerId for plugin execution default-compile: PluginResolutionException: Plugin org.apache.maven.plugins:maven-compiler-plugin:3.1
    Spring容器加载配置文件
    Java基础(一)字符串之如何删除指定的字符
    Java基础(二)数组之如何排序数组并插入元素
    Java基础(一)字符串之如何比较字符串
    Spring的事务管理
    Java使用split()截取字符串
    JavaWeb过滤器实现页面跳转至登录页面
    jQuery获取session中的值
  • 原文地址:https://www.cnblogs.com/ilverene/p/9819044.html
Copyright © 2020-2023  润新知