Educational DP Contest U - Grouping 状压dp

传送门

题意:

给你 n n n个物品,让你将其分成任意组,在同一个组内的 i , j i,j i,j会获得 a i , j a_{i,j} ai,j的收益,让你选择一种分组方案使得收益最大。

1 ≤ n ≤ 16 , ∣ a i , j ∣ ≤ 1 e 9 1\le n\le 16,|a_{i,j}|\le 1e9 1n16,ai,j1e9

思路:

考虑到 n n n很小,所以考虑状压 d p dp dp,设 d p [ i ] dp[i] dp[i]代表选的数状态为 i i i的时候的最大收益,那么下面的问题就是如何合并转移了。。

我们利用像区间 d p dp dp一样的思路枚举断点,这里是枚举当前状态 s t a t e state state的子集,设枚举的子集是 i i i,那么 s t a t e − i state-i statei就是另一个子集,我们只要保证算 s t a t e state state的时候其子集的最优解已经被求出来即可,转移就是 f [ s t a t e ] = m a x ( f [ s t a t e ] , f [ i ] + f [ s t a t e − i ] ) f[state]=max(f[state],f[i]+f[state-i]) f[state]=max(f[state],f[i]+f[statei])

复杂度 O ( n 2 2 n + 3 n ) O(n^22^n+3^n) O(n22n+3n),因为枚举子集的总复杂度是 3 n 3^n 3n

#include
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define Mid (tr[u].l+tr[u].r>>1)
#define pb push_back
using namespace std;

const int N=20,INF=0x3f3f3f3f,mod=1e9+7;
typedef long long LL;

int n;
int a[N][N];
LL dp[1<<N],val[1<<N];

void solve() {
    cin>>n;
    for(int i=0;i<n;i++) {
        for(int j=0;j<n;j++) {
            cin>>a[i][j];
        }
    }
    for(int state=1;state<1<<n;state++) {
        for(int i=0;i<n;i++) {
            for(int j=i+1;j<n;j++) if((state>>i&1)&&(state>>j&1)) {
                val[state]+=a[i][j];
            }
        }
    }
    for(int state=1;state<1<<n;state++) {
        dp[state]=val[state];
        for(int j=state&(state-1);j;j=(j-1)&state) {
            dp[state]=max(dp[state],dp[state^j]+dp[j]);
        }
    }
    cout<<dp[(1<<n)-1]<<endl;
}

int main() {
	int _=1;
	while(_--) {
		solve();
	}

}

你可能感兴趣的:(dp,算法,c++,c语言)