2019 ICPC 南昌 Regional B. A Funny Bipartite Graph(狀壓DP)

題意

一個二分圖,左右各有nn個點,左邊第ii個點有一個屬性mim_i,它在一個圖中的價值爲midim_i^{d_i},其中did_i爲它在圖中的度數(特殊的,如果度數爲00,則價值爲00),求一個該二分圖的子圖使得右邊的每個點度數都不爲00且總價值最小,輸出最小价值。如果無解輸出1-1
有若干個限制條件(i,j)(i,j)表示子圖中左邊的點iijj不能同時存在
保證:

  1. 原二分圖中左邊的每個點度數在[1,3][1,3]之間。
  2. 左邊的ii點和右邊的jj點之間右邊當且僅當iji\le j

數據範圍n18,mi100n\le 18,m_i\le100

解題思路

這個數據範圍很容易想到狀壓DP,但是有一個困難就是它既需要前面選擇的左邊的點的狀態,又需要當前沒有選擇的右邊的點的狀態,如果都用二進制去枚舉,那麼複雜度是22n2^{2n}
注意到題目保證了:左邊的ii點和右邊的jj點之間右邊當且僅當iji\le j
那麼從小到大爲左邊的點選擇連邊,當前選擇點ii的時候,右邊編號爲[1,i1][1,i-1]的點肯定都被覆蓋了,而左邊編號爲[i+1,n]的點肯定都還沒選擇。那麼這兩個部分的二進制長度加起來剛好是n。
所以我們從小到大爲左邊的點選擇連邊進行狀態轉移,爲ii選擇連邊時,maskmask[0,i1][0,i-1]位代表前面選擇的左邊的點,[i,n][i,n]位表示已經選擇了的右邊的點。然後枚舉點ii連邊情況進行轉移即可。
代碼:

#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;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章