计蒜客葱头君的积木

问题描述

蒜头君酷爱搭积木,他用积木搭了 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]);

扫描二维码关注公众号,回复: 1061015 查看本文章
可用来枚举二进制i的子集j以及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;
}

猜你喜欢

转载自blog.csdn.net/lzy1380/article/details/80382809