问题描述
蒜头君酷爱搭积木,他用积木搭了 n 辆重量为 wi的小车和一艘最大载重量为 W 的小船,他想用这艘小船将 n 辆小车运输过河。每次小船运载的小车重量不能超过 W。另外,小船在运载小车时,每辆小车会对小船有一个损坏值si,当多辆小车一起运载时,该趟运载对小船的损坏值为船上所有小车的最大损坏值。
现在蒜头君想知道,如何用小船运载 n 辆小车,可以使得对小船造成的总损坏值最小。
输入格式
第一行输入两个数 W 和 n(100≤w≤400,1≤n≤16),分别表示小船的最大载重量和小车总数。
接下来输入 n 行,每行输入两个整数si和 wi(1≤si ≤50,10≤wi≤100),分别表示每辆小车对小船的损坏值和每辆小车的重量。
输出格式
输出一行,输出一个整数,表示用小船运载 n 辆小车,最小的总损坏值。
样例输入
90 4
32 50
15 20
40 50
13 40
样例输出
72
数据处理:
将船只按照花费从小到大排序。每艘船已经运过去设为1,没运过去设为0,则每种状态为一个二进制数,我们所要达到的状态即为(1<<n)-1(注意这个状态数)。
转移方程:
从1到(1<<n)-1循环状态cur。对cur,计算其已经运过去的重量和sum。若sum<=w,则可一次性运过去,其花费即损失值 的最大值即dp[cur]=max(cost[i])。若sum>w,则将其分成两个子集a和b(a|b=cur),因为a与b为子集所以在之前已经计算过所以有dp[cur]=min(dp[a]+dp[b])。
技巧:
for(int i=1;i<1<<n;i++)
for(int j=i;j;j=(j-1)&i)
dp[i]=min(dp[i],dp[j]+dp[j^i]);
代码
#include<bits/stdc++.h> using namespace std; const int N=50; const int MAX=1000000; int n,w,sum,maxx,cost[N],weight[N],dp[1<<17]; int main(){ scanf("%d %d",&w,&n); for(int i=1;i<=n;i++) scanf("%d %d",&cost[i],&weight[i]); for(int i=1;i<(1<<n);i++){ sum=0; maxx=0; for(int cur=i,j=1;cur;j++,cur=cur>>1)//计算当前状态的重量即一次性运过去的花费 if(cur&1) {sum+=weight[j]; maxx=max(maxx,cost[j]);} if(sum<=w) dp[i]=maxx; else{ dp[i]=MAX; for(int j=i;j;j=(j-1)&i){ dp[i]=min(dp[i],dp[j]+dp[j^i]); } } } printf("%d",dp[(1<<n)-1]); return 0; }