Bzoj: 2073 [POI2004]PRZ 题解
Posted Hzoi_joker
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bzoj: 2073 [POI2004]PRZ 题解相关的知识,希望对你有一定的参考价值。
2073: [POI2004]PRZ
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 401 Solved: 296
[Submit][Status][Discuss]
Description
一只队伍在爬山时碰到了雪崩,他们在逃跑时遇到了一座桥,他们要尽快的过桥. 桥已经很旧了, 所以它不能承受太重的东西. 任何时候队伍在桥上的人都不能超过一定的限制. 所以这只队伍过桥时只能分批过,当一组全部过去时,下一组才能接着过. 队伍里每个人过桥都需要特定的时间,当一批队员过桥时时间应该算走得最慢的那一个,每个人也有特定的重量,我们想知道如何分批过桥能使总时间最少.
Input
第一行两个数: w – 桥能承受的最大重量(100 <= w <= 400) 和 n – 队员总数(1 <= n <= 16). 接下来n 行每行两个数分别表示: t – 该队员过桥所需时间(1 <= t <= 50) 和 w – 该队员的重量(10 <= w <= 100).
Output
输出一个数表示最少的过桥时间.
Sample Input
100 3
24 60
10 40
18 50
24 60
10 40
18 50
Sample Output
42
一道有趣的DP题……
数据范围这么小,果断是状压DP啊,但是具体怎么DP呢?一开始想的是不断合并两个状态数组,然而表示我不会只枚举所有合法子集的方法,如果暴力枚举0~i会超时(事实证明真的会超,超15%)。然后想能否像愤怒的小鸟那样,然而更不靠谱。于是乎我一怒之下靠dfs枚举所有合法状态,觉得能过60分就很不错了,结果万万没想到,A了???490ms??
然后上网看题解,果然,就是枚举当前状态的所有子集,只不过我们不用暴力,用的是位运算:
1 for(int j=i;j;j=(j-1)&i) 2 { 3 if(sum[j]<=W) f[i]=min(f[i],ti[j]+f[i^j]); 4 }
j枚举出来就是i的所有子集。因此,我们还得预处理出来所有状态的总重量以及时间。然后就相当好搞了。据说复杂度可以证明为3^n。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #include <cmath> 7 #include <queue> 8 #include <map> 9 #include <set> 10 #include <vector> 11 #define N 20 12 using namespace std; 13 int f[1<<17]; 14 int n,t[N],w[N],W; 15 int sum[1<<17],ti[1<<17]; 16 int main() 17 { 18 scanf("%d%d",&W,&n); 19 for(int i=1;i<=n;i++) 20 { 21 scanf("%d%d",&t[i],&w[i]); 22 } 23 memset(f,0x7f,sizeof(f)); 24 f[0]=0; 25 for(int i=1;i<(1<<n);i++) 26 { 27 for(int j=1;j<=n;j++) 28 { 29 if((1<<(j-1))&i) 30 { 31 sum[i]+=w[j]; 32 ti[i]=max(ti[i],t[j]); 33 } 34 } 35 } 36 for(int i=1;i<(1<<n);i++) 37 { 38 for(int j=i;j;j=(j-1)&i) 39 { 40 if(sum[j]<=W) f[i]=min(f[i],ti[j]+f[i^j]); 41 } 42 } 43 printf("%d\n",f[(1<<n)-1]); 44 return 0; 45 }
以上是关于Bzoj: 2073 [POI2004]PRZ 题解的主要内容,如果未能解决你的问题,请参考以下文章