题意:
给你 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 1≤n≤16,∣ai,j∣≤1e9
思路:
考虑到 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 state−i就是另一个子集,我们只要保证算 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[state−i])。
复杂度 O ( n 2 2 n + 3 n ) O(n^22^n+3^n) O(n22n+3n),因为枚举子集的总复杂度是 3 n 3^n 3n。
#include<bits/stdc++.h>
#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();
}
}