1. 程式人生 > >“東信杯”廣西大學第一屆程式設計競賽(同步賽)F-出裝方案(二分圖最大匹配/狀壓dp)

“東信杯”廣西大學第一屆程式設計競賽(同步賽)F-出裝方案(二分圖最大匹配/狀壓dp)

題目

思路來源

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=37522548(MCMF做法待補)

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=37514575(狀壓dp)

心得

本來就是一個二分圖最大權匹配的KM板子題,上了板子就過了。

但是,看到了一個高中生的狀壓dp做法,

不得不說,有些高中生真的認真,程式碼通俗易懂可讀性強,

的確是一步一個腳印紮紮實實寫的程式碼(嗯白膜法師這種已經不能叫高中生了)

所以,藉著人家的程式碼學一學從沒學過的狀壓dp,mcmf回頭再學吧

QAQ睡個覺果然讀程式碼能力強了不少,以後看不懂程式碼要及時休息啊,效率++

程式碼

#include <cstdio>
#include <cstring>
#include <algorithm>
 
int a[110][110];
int dp[1 << 15];
 
int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        int n; scanf("%d", &n);
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++) scanf("%d", &a[i][j]);
        memset(dp, 0, sizeof(dp));
        for(int S = 1; S < (1 << n); S++)
        {
            int tot = 0;
            for(int i = 0; i < n; i++) tot += ((S >> i) & 1);
            tot--;
            for(int i = 0; i < n; i++)
            {
                if((S >> i) & 1)
                    dp[S] = std::max(dp[S], dp[S ^ (1 << i)] + a[tot][i]);
            }
        }
        printf("%d\n", dp[(1 << n) - 1]);
    }
    return 0;
}

個人理解

先把人家的程式碼粘過來。

狀壓dp是二進位制列舉思想,用第i位來列舉第i件物品,

1代表該物品已被選,不能再選了;0代表該物品尚未被選。

tot是列舉的二進位制數中的1的個數,第tot個人可以在尚未被選的裡面選一個。

為了與下標對應,tot減1。

核心句

if((S >> i) & 1)dp[S] = std::max(dp[S], dp[S ^ (1 << i)] + a[tot][i]);

感覺這個很形象啊QAQ,

第tot個人知道,當他選第i件商品的時候,能達成tot個人之和的最優,

於是他讓前tot-1個人別選第i件商品了,“都讓讓都讓讓,你們從剩下tot-1件商品裡挑,把第i件給我"。

我寫部落格咋變成這個畫風了

然而我們不知道第tot個人選哪件能達成最優,

所以我們就要在狀壓的某狀態(如01010101)的某個1值裡,列舉一件給第tot個人。

 

即n個人分n件商品的最優,一定是(n-1)個人分其中(n-1)個商品的最優,加上最後一個人選剩下一件商品所求得。

狀壓dp,好就好在當第i位為1時,把第i位變0原值會變小,所以大一定從小轉移而來。

因為有這個最優子結構的性質,tot個人的最優,一定是由tot-1個人的某種最優選法轉移而來,故有等式成立。

 

如11可以由01或10轉移而來,

前一種轉移代表第一個人選了第二件而第二個人選了第一件,

後一種轉移代表第一個人選了第一件而第二個人選了第二件,

但是既然這兩種已經被前兩個人選了,我們只用11保留這兩種情況的最大值即最優情況。

 

其實這就跟遞迴一樣,你不知道某個狀態的值,甚至其子狀態的值,

但是最底層的值和轉移方程你是知道的,所以你就知道了一切.jpg。

 

狀壓dp一般很明顯,O(2^n)所以一般n不超過15。

當然KM演算法O(n^3)在這裡顯得優了好多。

只是學習一波狀壓dp罷了。

傳說中的dp一題能想一晚上真不是吹的啊