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,自己对这个优化很满意啊,但是咋就这么笨,必须一点点的想起来啊)
代码如下:
View Code
1 /* 2 数组one记录每个状态1的个数, 3 数组bull记录每头牛喜欢的仓库, 4 bull[i][0]记录第i头牛喜欢仓库的个数, 5 仓库的编号是0~n-1。 6 */ 7 8 #include <stdio.h> 9 10 const int maxs = 1100000; 11 const int maxn = 22; 12 13 int dp[maxs], none[maxs]; 14 int bull[maxn][maxn]; 15 16 int main () { 17 18 int n, m; 19 20 scanf("%d%d", &n, &m); 21 for (int i = 1, tmp; i <= n; i++) { 22 scanf("%d", &tmp); 23 bull[i][0] = tmp; 24 for (int j = 1; j <= bull[i][0]; j++) { 25 scanf("%d", &bull[i][j]); 26 bull[i][j]--; // barn的标号为0~n-1 27 } 28 } 29 int M = 1 << m; 30 for (int i = 0; i < M; i++) { 31 int num = 0; 32 for (int j = 0; j < m; j++) { 33 if (i & (1<<j)) { 34 num++; 35 } 36 } 37 none[i] = num; 38 } 39 for (int i = 1; i <= bull[1][0]; i++) { 40 int x = bull[1][i]; 41 dp[1<<x] = 1; 42 } 43 for (int i = 2; i <= n; i++) { 44 for (int j = M-1; j >= 0; j--) { 45 if (none[j] != i) continue; 46 for (int k = 1; k <= bull[i][0]; k++) { 47 int x = bull[i][k]; 48 if (!(j & (1<<x))) continue; 49 dp[j] += dp[j-(1<<x)]; 50 } 51 } 52 } 53 int ans = 0; 54 for (int i = 0; i < M; i++) { 55 if (none[i] == n) { 56 ans += dp[i]; 57 } 58 } 59 printf("%d\n", ans); 60 61 return 0; 62 }
保持本心,眼界放宽,做好每一件事,每天有所收获。