【计蒜客习题】蒜头君的积木

问题描述

 

蒜头君酷爱搭积木,他用积木搭了 n 辆重量为 wi的小车和一艘最大载重量为 W 的小船,他想用这艘小船将 n 辆小车运输过河。每次小船运载的小车重量不能超过 W。另外,小船在运载小车时,每辆小车会对小船有一个损坏值si,当多辆小车一起运载时,该趟运载对小船的损坏值为船上所有小车的最大损坏值。
现在蒜头君想知道,如何用小船运载 n 辆小车,可以使得对小船造成的总损坏值最小。


输入格式


第一行输入两个数 W 和 n(100≤w≤400,1≤n≤16),分别表示小船的最大载重量和小车总数。
接下来输入 n 行,每行输入两个整数si和 wi(1≤si ≤50,10≤wi≤100),分别表示每辆小车对小船的损坏值和每辆小车的重量。
输出格式 
输出一行,输出一个整数,表示用小船运载 nn 辆小车,最小的总损坏值。


样例输入


90 4
32 50
15 20
40 50
13 40


样例输出


72


 

状压DP里的经典做法,枚举子集。

for(int j=0;j<(1<<n);++j)
    for(int k=j;k;k=(k-1)&j)

仔细揣摩一下上面的代码,这样枚举子集的效率是很高的。请记住!

对于这道题很容易想到,对于要求解的任意一个方案(第i辆搬不搬),可以通过其子集求解,我们枚举其子集代表达成此方案的最后一次运载,取补集就是之前完成的,这样dp[j]=min{dp[j-k]+cost[k]}。为了提高效率我们可以先预处理出每种情况的花费。

 1 #include<cstdio>
 2 #include<cstring>
 3 inline int min(int a,int b) {return a<b?a:b;}
 4 inline int max(int a,int b) {return a>b?a:b;}
 5 const int maxw=405,maxn=20,inf=0x3f3f3f3f;
 6 int W,n,s[maxn],w[maxn],dp[1<<maxn],weight[1<<maxn],cost[1<<maxn];
 7 int get_w(int i) {
 8     int ans_w=0;
 9     for(int j=1;j<=n;++j) {
10         if(i&1) ans_w+=w[j];
11         i>>=1;
12     }
13     return ans_w;
14 }
15 int get_c(int i) {
16     int ans_c=0;
17     for(int j=1;j<=n;++j) {
18         if(i&1) ans_c=max(ans_c,s[j]);
19         i>>=1;
20     }
21     return ans_c;
22 }
23 int main() {
24     scanf("%d%d",&W,&n);
25     for(int i=1;i<=n;++i) scanf("%d%d",&s[i],&w[i]);
26     memset(dp,inf,sizeof(dp));
27     for(int i=0;i<(1<<n);++i) weight[i]=get_w(i),cost[i]=get_c(i);
28     dp[0]=0;
29     for(int j=1;j<(1<<n);++j) {
30         for(int k=j;k;k=(k-1)&j) {
31             if(weight[k]<=W)
32                 dp[j]=min(dp[j],dp[j-k]+cost[k]);
33         }
34     }
35     printf("%d",dp[(1<<n)-1]);
36     return 0;
37 }
AC代码

 

posted @ 2018-09-12 18:56  Mr^Kevin  阅读(609)  评论(0编辑  收藏  举报