按照插入数的大小排序,
然后依次进行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; }