HDU - 1059 与 POJ - 3260 结合学习,总结多重背包经验

HDU - 1059
Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal share of the marbles. This would be easy if all the marbles had the same value, because then they could just split the collection in half. But unfortunately, some of the marbles are larger, or more beautiful than others. So, Marsha and Bill start by assigning a value, a natural number between one and six, to each marble. Now they want to divide the marbles so that each of them gets the same total value.
Unfortunately, they realize that it might be impossible to divide the marbles in this way (even if the total value of all marbles is even). For example, if there are one marble of value 1, one of value 3 and two of value 4, then they cannot be split into sets of equal value. So, they ask you to write a program that checks whether there is a fair partition of the marbles.
Input
Each line in the input describes one collection of marbles to be divided. The lines consist of six non-negative integers n1, n2, …, n6, where ni is the number of marbles of value i. So, the example from above would be described by the input-line ``1 0 1 2 0 0’’. The maximum total number of marbles will be 20000.

The last line of the input file will be 0 0 0 0 0 0''; do not process this line. **Output** For each colletcion, outputCollection #k:’’, where k is the number of the test case, and then either Can be divided.'' orCan’t be divided.’’.

Output a blank line after each test case.
Sample Input
1 0 1 2 0 0
1 0 0 0 1 1
0 0 0 0 0 0
Sample Output
Collection #1:
Can’t be divided.

Collection #2:
Can be divided.

玛莎和比尔拥有一批大理石。他们想把收藏品分成两部分,这样双方都能得到同等份额的大理石。如果所有的大理石都有相同的价值,这就很容易了,因为这样它们就可以将收藏分成两半。但不幸的是,有些大理石比其他的大,或更漂亮。所以,玛莎和比尔首先给每个大理石指定一个值,一个介于1到6之间的自然数。现在他们想把这些弹珠分开,这样每个弹珠的总价值都是一样的。

不幸的是,他们意识到用这种方式划分大理石可能是不可能的(即使所有大理石的总价值都是偶数)。例如,如果有一个值为1的大理石、值为3的大理石和值为4的大理石,则不能将它们拆分为相等值的集合。所以,他们要求你写一个程序来检查大理石是否有一个公平的划分。

思路:
1.每个人要求分总价值的一半的弹珠,也就是说,背包的容量其实和价值是一样的,都是total/2,也就是说dp的容量就是total/2,然后最后看dp[total/2]==total/2。
2.这很明显是一个多重背包价值最大化问题,题目的隐藏点第一点已经说明白了,也就是容量就是价值,能不能刚好凑齐total/2,所以一定是求最大值
3.多重背包,对于每一件物品而讲,如果num*value大于了total/2就变成了完全背包,如果小于则就成了01背包,此题只用一个dp数组就可,一个dp数组按照哪一种价值的弹珠而讨论用那种背包

AC代码

#include
#include
#include
using namespace std;
#define MAXN 20000*5
int num[7];
int val[7]={0,1,2,3,4,5,6};
int dp[MAXN];
int total;

void zero_one_pack(int cost, int val)
{
for(int i = total; i>=cost; i–)

    dp[i]=max(dp[i],dp[i-cost]+val);

}

扫描二维码关注公众号,回复: 8839738 查看本文章

void complete_pack(int cost, int val)
{
for(int i = cost; i<=total; i++)

    dp[i] = max(dp[i],dp[i-cost]+val);

}

void muti_pack(int cost, int val, int sum)
{
if(cost*sum >= total)//如果该物品的总价值大于了total的话,不就成了完全背包了吗,你可以随便选
{
complete_pack(cost,val);

    return;
}


else//否则就是  01pack
{
    int k = 1;

    while(k < sum)
    {
        zero_one_pack(k*cost, k*val);

        sum -= k;

        k *= 2;
    }

    zero_one_pack(sum*cost, sum*val);
}


return;

}

int main()
{
int sum;

int s=1;

for(int i=1; i<=6; i++)

    scanf("%d",&num[i]);

while(num[1]!=0||num[2]!=0||num[3]!=0||num[4]!=0||num[5]!=0||num[6]!=0)
{
    printf("Collection #%d:\n",s++);

    sum = 0;

    for(int i=1; i<=6; i++)

        sum += val[i]*num[i];

    if(sum%2==1)

        printf("Can't be divided.\n\n");

    else if(sum%2==0)
    {
        total = sum/2;

        for(int i=1; i<=6; i++)//遍历每一种弹珠,然后进入多重背包子函数进行判断使用那种背包

            muti_pack(val[i],val[i],num[i]);

        printf("%s\n\n",total==dp[total]?"Can be divided.":"Can't be divided.");

    }

    for(int i=1; i<=6; i++)

        scanf("%d",&num[i]);
}

return 0;

}

POJ - 3260

Farmer John has gone to town to buy some farm supplies. Being a very efficient man, he always pays for his goods in such a way that the smallest number of coins changes hands, i.e., the number of coins he uses to pay plus the number of coins he receives in change is minimized. Help him to determine what this minimum number is.

FJ wants to buy T (1 ≤ T ≤ 10,000) cents of supplies. The currency system has N (1 ≤ N ≤ 100) different coins, with values V1, V2, …, VN (1 ≤ Vi ≤ 120). Farmer John is carrying C1 coins of value V1, C2 coins of value V2, …, and CN coins of value VN (0 ≤ Ci ≤ 10,000). The shopkeeper has an unlimited supply of all the coins, and always makes change in the most efficient manner (although Farmer John must be sure to pay in a way that makes it possible to make the correct change).

Input
Line 1: Two space-separated integers: N and T.
Line 2: N space-separated integers, respectively V 1, V 2, …, VN coins ( V 1, … VN)
Line 3: N space-separated integers, respectively C 1, C 2, …, CN
Output
Line 1: A line containing a single integer, the minimum number of coins involved in a payment and change-making. If it is impossible for Farmer John to pay and receive exact change, output -1.
Sample Input
3 70
5 25 50
5 2 1
Sample Output
3
Hint
Farmer John pays 75 cents using a 50 cents and a 25 cents coin, and receives a 5 cents coin in change, for a total of 3 coins used in the transaction.

农夫约翰到城里去买一些农具。作为一个非常有效率的人,他总是以这样一种方式来支付他的货物:最少数量的硬币易手,即他用来支付的硬币数量加上他在兑换中收到的硬币数量最小化。帮助他确定这个最小值是多少。
FJ想购买T(1到T到10000)美分的供应品。货币系统有n(1-n-100)种不同的硬币,价值为v1、v2、vn(1-vi-120)。农夫约翰携带着价值为v1的c1币、价值为v2的c2币和价值为vn(0-ci-10000)的cn币。店主对所有的硬币都有无限的供应,并且总是以最有效的方式进行更改(尽管农场主约翰必须确保支付的方式能够进行正确的更改)。

思路:
1.为什么我们说农夫是多重背包,而商家是完全背包呢,其实很简单啊
农夫支付的钱一定是T+i,而商家所需要找的钱一定是i,而两个人所需要面对的问题都是同一个问题,对某一价值提供最少数量的硬币,他们的不同就在于农夫的钱有限而商家的钱无限。
2.价值与代价
我们求得是什么,什么就是价值,所以硬币的数量就是价值,所以每一种硬币的价值都是1,而代价呢就是硬币的价值,因为我们要凑够T+i的钱,所以T+i就是背包容量,固硬币的价值就是代价。
3.初始化问题
我们习惯于解决背包中物品价值最大化的问题,而很少去解决最小化的问题,最大化问题初始化很简单,就是memset(dp,0,sizeof(dp))就ok了,但是最小化问题需要的是memset(dp,INF,sizeof(dp)),然后还需要一句dp[0] = 0,这样才算是初始化完成
4.dp数组数量的问题
因为我们解决的问题是分为农夫和商家两部分,固我们应该找两个dp数组为好

**AC代码
#include
#include
#include
using namespace std;
#define MAXN 110
#define MAXV 130
#define MAXT 10000
#define INF 0x3f3f3f
int n,t;
int v[MAXN],c[MAXN];
int dp_pay[MAXT+MAXVMAXV];
int dp_change[MAXT+MAXV
MAXV];
int maxv;
void complete_pack(int n, int w)
{
memset(dp_change,INF,sizeof(dp_change));

dp_change[0] = 0;

for(int i = 1; i <= n; i++)
	
	for(int j = v[i]; j <= w; j++)
	
		dp_change[j] = min(dp_change[j],dp_change[j-v[i]]+1);

}
void muti_pack(int n, int w)//注意这里很重要,这里的不同和上面的Dividing的多重背包出现了不一样的地方,就是说,本题并未考虑价值和数量的乘积是否大于了背包的容量,而是统一用了二进制分割,其实可以想一下,的确是这样的,如果超了,放不进去了,自然也就不进去了
{
memset(dp_pay,INF,sizeof(dp_pay));

dp_pay[0] = 0;//初始化

for(int i = 1; i <= n; i++)
{
	int num = c[i];
	
	for(int k = 1; num > 0; k <<= 1)
	{
		int mul = min(num,k);
		
		for(int j = w; j >= v[i] * mul; j--)
		{
			dp_pay[j] = min(dp_pay[j],dp_pay[j-v[i]*mul]+mul);
		}
		
		num -= mul;
	}
}

}
void solve()
{
int ans = INF;

muti_pack(n,t+maxv*maxv);

complete_pack(n,t+maxv*maxv);

for(int i = 0; i <= maxv*maxv; i++)
{
	ans = min(ans,dp_pay[t+i]+dp_change[i]);
}

if(ans == INF) ans = -1;

printf("%d\n",ans);

}
int main()
{
maxv = 0;

scanf("%d %d",&n,&t);

for(int i = 1; i <= n; i++)
{
	scanf("%d",&v[i]);
	
	maxv = max(maxv,v[i]);
}
	
for(int i = 1; i <= n; i++)

	scanf("%d",&c[i]);

solve();

return 0;

}
**

发布了35 篇原创文章 · 获赞 0 · 访问量 955

猜你喜欢

转载自blog.csdn.net/ACM_Cmy/article/details/98488279