51nod 1779逆序对统计(状压DP)

按照插入数的大小排序,

然后依次进行dp。

用一个状态表示n个数是否被选了

10110 就是表示第1、3、4个位置都选了

那么如果此时这个数该填到5这个位置,那么必定会造成一个逆序(因为下一个数会填到2,下一个数必定比这个数大)

也就是转移的时候看插入位置前有多少个0,进行转移

写的时候有一些小技巧

(直接用记忆化统计0的个数超时了,这里有一个递推的技巧)

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int dp[2][(1<<20)+1], f[(1<<20)+1];
int n, m, a[111];

int main()
{
    cin>>n>>m;
    for(int i = 1; i <= m; i++) cin>>a[i];
    for(int i = 0; i < n; i++) f[1<<i] = 1;
    for(int i = 0; i < (1<<n); i++) f[i] = f[i&-i]+f[i^(i&-i)];
    memset(dp[0], 128, sizeof(dp[0]));
    dp[0][0] = 0;
    int all = (1<<n)-1;
    for(int i = 1; i <= m; i++){
        for(int s = 0; s <= all; s++){
            if(dp[(i-1)&1][s] < 0) continue;
            dp[i&1][s] = max(dp[i&1][s], dp[(i-1)&1][s]);
            if((s&(1<<(n-a[i]))) == 0){
                dp[i&1][s|(1<<(n-a[i]))] = max(dp[i&1][s|(1<<(n-a[i]))], dp[(i-1)&1][s] + (a[i]-1-f[s>>(n-a[i]+1)]));
            }
        }
        memset(dp[(i-1)&1], 128, sizeof(dp[(i-1)&1]));
    }
    cout<<dp[m&1][(1<<n)-1]<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/Saurus/p/6864235.html