传送门
题意:
给你
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();
}
}
|