题意
一个二分图,左右各有
n
n
n个点,左边第
i
i
i个点有一个属性
m
i
m_i
mi,它在一个图中的价值为
m
i
d
i
m_i^{d_i}
midi,其中
d
i
d_i
di为它在图中的度数(特殊的,如果度数为
0
0
0,则价值为
0
0
0),求一个该二分图的子图使得右边的每个点度数都不为
0
0
0且总价值最小,输出最小价值。如果无解输出
−
1
-1
−1
有若干个限制条件
(
i
,
j
)
(i,j)
(i,j)表示子图中左边的点
i
i
i和
j
j
j不能同时存在
保证:
- 原二分图中左边的每个点度数在 [ 1 , 3 ] [1,3] [1,3]之间。
- 左边的 i i i点和右边的 j j j点之间右边当且仅当 i ≤ j i\le j i≤j
数据范围 n ≤ 18 , m i ≤ 100 n\le 18,m_i\le100 n≤18,mi≤100
解题思路
这个数据范围很容易想到状压DP,但是有一个困难就是它既需要前面选择的左边的点的状态,又需要当前没有选择的右边的点的状态,如果都用二进制去枚举,那么复杂度是
2
2
n
2^{2n}
22n。
注意到题目保证了:左边的
i
i
i点和右边的
j
j
j点之间右边当且仅当
i
≤
j
i\le j
i≤j
那么从小到大为左边的点选择连边,当前选择点
i
i
i的时候,右边编号为
[
1
,
i
−
1
]
[1,i-1]
[1,i−1]的点肯定都被覆盖了,而左边编号为[i+1,n]的点肯定都还没选择。那么这两个部分的二进制长度加起来刚好是n。
所以我们从小到大为左边的点选择连边进行状态转移,为
i
i
i选择连边时,
m
a
s
k
mask
mask的
[
0
,
i
−
1
]
[0,i-1]
[0,i−1]位代表前面选择的左边的点,
[
i
,
n
]
[i,n]
[i,n]位表示已经选择了的右边的点。然后枚举点
i
i
i连边情况进行转移即可。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int inf = 0x3f3f3f3f;
int dp[2][1<<18];
int val[20], ban[20];
vector<int> g[20];
char s[20];
int n;
void init(){
scanf("%d", &n);
for(int i = 0; i < n; ++i) g[i].clear();
for(int i = 0; i < n; ++i) {
scanf("%s", s);
for(int j = 0; j < n; ++j) if(s[j] == '1') g[i].push_back(j);
}
for(int i = 0; i < n; ++i) {
scanf("%s", s); ban[i] = 0;
for(int j = 0; j < i; ++j) if(s[j] == '1') ban[i] |= (1<<j);
}
for(int i = 0; i < n; ++i) scanf("%d", &val[i]);
}
int sol(){
int cur = 0, nxt = 1;
memset(dp, 0x3f, sizeof dp);
dp[cur][0] = 0;
for(int i = 0; i < n; ++i){
for(int mask = 0; mask < (1<<n); ++mask){
int lstate = mask&((1<<i)-1);
int rstate = mask&((1<<n)-(1<<i));
if(dp[cur][mask] == inf) continue;
// don't choose i
if(rstate>>i&1) dp[nxt][(mask)^(1<<i)] = min(dp[nxt][(mask)^(1<<i)], dp[cur][mask]);
if(ban[i]&lstate) continue;//can't choose i
for(int t = 1; t < (1<<g[i].size()); ++t){
int cost = 1;
int ex = 0;
for(int j = 0; j < g[i].size(); ++j){
int v = g[i][j];
if(t>>j&1) cost *= val[i], ex |= 1<<v;
}
int nstate = rstate|ex;
if( !(nstate>>i&1) ) continue;
dp[nxt][lstate|nstate] = min(dp[nxt][lstate|nstate], dp[cur][mask] + cost);
}
}
swap(cur, nxt);
memset(dp[nxt], 0x3f, sizeof dp[nxt]);
}
int ans = inf;
for(int i = 0; i < (1<<n); ++i) ans = min(ans, dp[cur][i]);
if(ans == inf) return -1;
return ans;
}
int main()
{
int T;cin>>T;
while(T--){
init();
cout<<sol()<<endl;
}
}