hdu 1171 (背包或者母函数问题)

Problem Description
Nowadays, we all know that Computer College is the biggest department in HDU. But, maybe you don't know that Computer College had ever been split into Computer College and Software College in 2002.
The splitting is absolutely a big event in HDU! At the same time, it is a trouble thing too. All facilities must go halves. First, all facilities are assessed, and two facilities are thought to be same if they have the same value. It is assumed that there is N (0<N<1000) kinds of facilities (different value, different kinds).
 
Input
Input contains multiple test cases. Each test case starts with a number N (0 < N <= 50 -- the total number of different facilities). The next N lines contain an integer V (0<V<=50 --value of facility) and an integer M (0<M<=100 --corresponding number of the facilities) each. You can assume that all V are different.
A test case starting with a negative integer terminates input and this test case is not to be processed.
 
Output
For each case, print one line containing two integers A and B which denote the value of Computer College and Software College will get respectively. A and B should be as equal as possible. At the same time, you should guarantee that A is not less than B.
 

Sample Input

2
10 1
20 1
3
10 1
20 2
30 1
-1
 
Sample Output
20 10
40 40
 
题意:大概就是给你一堆东西,第一行数据表示东西的种类,而后面再输入两个数,第一个代表物品的价值(v) ,第二个代表物品的个数(n),然后需要你开始按照价值分配给 a 和 b 两个组,同时要求 a ,b 两组后面的价值要尽可能的接近,并且保证 a 组的所获得价值不小于 b 组所获得的价值。
 
解题思路
刚拿到这题的时候觉得应该是背包的一种,首先 01背包是每种物品只能取一次,完全背包每种物品能取无限多次数。而这题可以把看作01背包来求解,就是每个价值相同的多件物品可以拆分成一件一件的看作不同的物品,就是数据有点大可能会超时。
所以先尝试一下,结果a了
附上ac代码
#include<iostream>
#include<algorithm>
#include<cstring> 
using namespace std;
int v[5001]={0};
int f[1000000]={0};
int main()
{
    int t;
    while(cin>>t&&t>0)
    {
        int sum=0;
        int count=0;
        memset(v,0,sizeof(v));
        memset(f,0,sizeof(f));
        while(t--)
        {
            int n,m;
            cin>>n>>m;
            for(int i=0;i<m;i++)
            {
                v[count++]=n;
                sum+=n;
            }
        }
        for(int i=0;i<count;i++)
        {
            for(int j=sum/2;j>=v[i];j--)
            {
                f[j]=max(f[j],f[j-v[i]]+v[i]);
            }
        }
        cout<<sum-f[sum/2]<<" "<<  f[sum/2]<<endl;
    }
    return 0;
} 

后面上网查了一下,发现了一个什么新概念,母函数的方法用来处理这种有一个或者多个的题目,具体的什么也没怎么看懂,感觉挺复杂的样子,但是这种题相当于一种模板题目,从大佬博客里面抄来的模板。

//为计算结果,b为中间结果。
int a[MAX],b[MAX];
//初始化a
memset(a,0,sizeof(a));
a[0]=1;
for (int i=1;i<=17;i++)//循环每个因子
{
    memset(b,0,sizeof(b));
    for (int j=n1[i];j<=n2[i]&&j*v[i]<=P;j++)//循环每个因子的每一项
        for (int k=0;k+j*v[i]<=P;k++)//循环a的每个项
            b[k+j*v[i]]+=a[k];//把结果加到对应位
    memcpy(a,b,sizeof(b));//b赋值给a
}

P是可能的最大指数。拿钞票组合这题来说,如果要求15元有多少组合,那么P就是15;如果问最小的不能拼出的数值,那么P就是所有钱加起来的和。P有时可以直接省略。

 如果n2是无穷,那么第二层循环条件j<=n2[i]可以去掉。

还有一种提高效率的模板2:

用一个last变量记录目前最大的指数,这样只需要在0..last上进行计算。

//初始化a,因为有last,所以这里无需初始化其他位
a[0]=1;
int last=0;
for (int i=0;i<K;i++)
{
    int last2=min(last+n[i]*v[i],P);//计算下一次的last
    memset(b,0,sizeof(int)*(last2+1));//只清空b[0..last2]
    for (int j=n1[i];j<=n2[i]&&j*v[i]<=last2;j++)//这里是last2
        for (int k=0;k<=last&&k+j*v[i]<=last2;k++)//这里一个是last,一个是last2
            b[k+j*v[i]]+=a[k];
    memcpy(a,b,sizeof(int)*(last2+1));//b赋值给a,只赋值0..last2
    last=last2;//更新last
}

 

而套用模板二ac的本题代码

#include <iostream>
#include <cstring>
using namespace std;
#define MAX 250010
int n,a[MAX],b[MAX],i,j,k,last,last2,v[50],m[50];
int main()
{
    while ((cin>>n)&&n>=0)
    {
        for (i=0;i<n;i++)
            cin>>v[i]>>m[i];
        a[0]=1;
        last=0;
        for (i=0;i<n;i++)
        {
            last2=last+m[i]*v[i];
            memset(b,0,sizeof(int)*(last2+1));
            for (j=0;j<=m[i];j++)
                for (k=0;k<=last;k++)
                    b[k+j*v[i]]+=a[k];
            memcpy(a,b,sizeof(int)*(last2+1));
            last=last2;
        }
        for (i=last/2;i>=0&&a[i]==0;i--);
        cout<<last-i<<' '<<i<<endl;
    }
    return 0;
}

 

原文地址:https://www.cnblogs.com/wushengyang/p/9366901.html