poj 2441Arrange the Bulls解題報告-狀態壓縮dp

題目鏈接:http://poj.org/problem?id=2441

題目描述:有n頭牛, m個倉庫,每頭牛有它喜歡的倉庫,每個倉庫最多隻能安排一頭牛,問有多少種安排方法。

 

這道題目應該算是狀態壓縮dp的簡單題目。

如果之前沒有接觸過狀態壓縮dp,或者沒有很好的理解的狀態壓縮dp,可以看看這兩篇論文:論文1, 論文2。這兩篇論文自我感覺都寫的很好,尤其是論文2裏講解的那三道題,很不錯。

求解:

1>  設計狀態:dp[i , j]表示前i頭牛形成狀態集合j的方法數。

2>  狀態轉移:dp[i , j] = sum{dp[i-1 , j-(1<<k)]},其中k表示第i頭牛可以放在倉庫k,則前i-1頭牛形成了狀態集合j-(1<<k)。

時間複雜度:(n*m*2^m),狀態數:(n*2^m)。

這道題可以在空間上優化,類似於01揹包的優化,從後向前推,具體見代碼。

(ps:一開始沒有優化,內存快到50M,是其他人的5-10倍,後來一想可以有滾動數組優化掉,就到了12000K,最後可以類似於01揹包空間優化一樣,倒着推就可以優化到8000k,自己對這個優化很滿意啊,但是咋就這麼笨,必須一點點的想起來啊)

代碼如下:

/*  
     數組one記錄每個狀態1的個數,
     數組bull記錄每頭牛喜歡的倉庫,
     bull[i][0]記錄第i頭牛喜歡倉庫的個數,
     倉庫的編號是0~n-1。
 */
 
 #include <stdio.h>
 
 const int maxs = 1100000;
 const int maxn = 22;
 
 int dp[maxs], none[maxs];
 int bull[maxn][maxn];
 
 int main () {
 
     int n, m;
 
     scanf("%d%d", &n, &m);
     for (int i = 1, tmp; i <= n; i++) {
         scanf("%d", &tmp);
         bull[i][0] = tmp;
         for (int j = 1; j <= bull[i][0]; j++) {
             scanf("%d", &bull[i][j]);
             bull[i][j]--;            // barn的標號爲0~n-1
         }
     }
     int M = 1 << m;
     for (int i = 0; i < M; i++) {
         int num = 0;
         for (int j = 0; j < m; j++) {
             if (i & (1<<j)) {
                 num++;
             }
         }
         none[i] = num;
     }
     for (int i = 1; i <= bull[1][0]; i++) {
         int x = bull[1][i];
         dp[1<<x] = 1;
     }
     for (int i = 2; i <= n; i++) {
         for (int j = M-1; j >= 0; j--) {
             if (none[j] != i) continue;
             for (int k = 1; k <= bull[i][0]; k++) {
                 int x = bull[i][k];
                 if (!(j & (1<<x))) continue;
                 dp[j] += dp[j-(1<<x)];
             }
         }
     }
     int ans = 0;
     for (int i = 0; i < M; i++) {
         if (none[i] == n) {
             ans += dp[i];
         }
     }
     printf("%d\n", ans);
 
     return 0;
 }

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