【codeforces gym101853E】Maximum Sum 状态dp经典问题

版权声明:若转载请附上原博客链接,谢谢! https://blog.csdn.net/Link_Ray/article/details/89102023

https://codeforces.com/gym/101853/problem/E

题意

给一个n×m的矩阵,要求选出一些点,使得这些点不相邻,同时这些点的和要最大。相邻的意义是,行或列或对角的距离等于1。
1 <= n <= 16

题解

一看到n的范围就想到状压dp,这和铺瓷砖很类似,就是一层一层的转移状态。如果直接暴力枚举状态的话复杂度是 O ( n 2 2 n ) O(n*2^{2n}) ,肯定要T的,遇到这种问题,就要想想如果只考虑单行,那么合法状态有多少种,直接提取出单行合法的进行转移,可以预处理出发现只有2000种,那么复杂度就降为 O ( n 4 e 6 ) O(n*4e6) 。同时还可以预处理出每行每种状态的权值和这样转移就只需要O(1)的时间。总时间复杂度是 O ( n 4 e 6 ) O(n*4e6)

代码

#include <bits/stdc++.h>

using namespace std;

int dp[18][1<<17];
int a[20][20];
int c[18][1<<17];
bool okrow(int x) {
    if(x&(x>>1))
        return false;
    return true;
}
bool ok(int x,int y) {
    if(x&y) return false;
    if(x&(y>>1) || (x>>1)&y) return false;
    return true;
}
int main() {
   // cout << 2500*2500 << endl;
    int t;
    scanf("%d", &t);
    vector<int> v;
    while(t--) {
        memset(dp, -0x3f3f3f, sizeof dp);
        memset(c,0,sizeof c);
        v.clear();
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            for(int j = 0; j < n; ++j)
                scanf("%d", &a[i][j]);


        int tot = (1<<n);
       // v.push_back(0);

        for(int i = 0; i < tot; ++i) {
            if(okrow(i)) {
                v.push_back(i);
              //  cout << i << endl;
            }
        }
        for(int i = 1; i <= n; ++i) {
            for(int j = 0; j < v.size(); ++j) {
                int t = v[j];
                for(int k = 0; k < n; ++k) {
                    if((1<<k)&t) {
                        c[i][j] += a[i][k];
                    }
                }
               // cout << i <<" " << v[j] <<" " << c[i][j] << endl;
            }
        }

        for(int i = 0; i < v.size(); ++i) {
            dp[1][i] = c[1][i];
        }
        for(int i = 2; i <= n; ++i) {
            for(int j = 0; j < v.size(); ++j) {
                for(int k = 0; k < v.size(); ++k) {
                    if(ok(v[j],v[k])) {
                           // cout << j <<" " << k << endl;
                  //      cout << i <<" " << j <<" " << k << " " << dp[i-1][k] << endl;
                        dp[i][j] = max(dp[i][j], dp[i-1][k]+c[i][j]);
                       // cout << i <<" " << v[j] <<" " <<" " << dp[i-1][k] <<" " << c[i][j] << " "<< dp[i][j] << endl;
                    }
                }
            }
        }

        //cout << v.size() << endl;
        int ans = 0;
        for(int i = 0; i < v.size(); ++i)
            ans = max(ans, dp[n][i]);
        cout << ans << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Link_Ray/article/details/89102023