背包问题经典实现方法

来源:互联网 发布:mysql explain 详解 编辑:程序博客网 时间:2024/06/06 12:56

    背包问题里“背包”的英文是 knopsack ,其基本问题就假设有一个限定重量的背包,若干一定重量的物品,每个物品都有各自的价值, 现在要尽可能的向背包里装入若干物品,使得背包中物品价值之和最大。这个问题在密码学上有重要的应用,有很多先进的算法,可惜我不懂……不过没关系,因为至少我们还有DP,可以使用空间复杂度O(n)时间复杂度O(n^2)的算法圆满实现该算法。

约定:背包的重量为c,物品重量为n,物品的重量为w[i],价值为v[i]。

基本的DP:

假设ks[i,j]表示在前i件物品中选取若干件(每件可以选取多次),使得包内装总重量为j时能得到的最大价值。则:

ks[0,0] = 0

ks[i,j] = max { ks[i-1,j-w[i]*k]+v[i]*k | 1<= k <= c/w[i] }

*如果每种物品只能拿一次(不可重复背包问题),去掉k。

真正实现的时候,由于考察第i种物品的时候只需要i-1有关的信息,所以只需要一个一维数组来储存。

来看一个(我认为)经典的DP程序:

(这段程序大概是这么来的:Somewhere(也许是Maigo的大脑)-->Maigo写的USACO标程-->MiniDragonXG同学消化+改编-->这里)

//以上省略部分代码
        for(i=0;i<=c;i++) ks[i]=-1;
        ks[
0]=0;
        
for(i=1;i<=n;i++){
                
for(j=w[i];j<=c;j++)  //改为for(j=c;j>=w[i];j--) 就能求不可重复背包问题
                        if(ks[j-w[i]]>=0 && ks[j]<ks[j-w[i]]+v[i]) 
                                     ks[j]=ks[j-w[i]]+v[i];
        }

        max
=-1;
        
for(i=0;i<=s;i++if(ks[i]>max) max=ks[i];

//max即为最大价值

这一段程序融合了背包状态存在性问题和可重复/不可重复问题,可谓做基本背包问题的居家良药啊-_-b

搜挂了3道有数据的题(贴在下面),一举拿下(也贴在下面),你也试试?(什么,你BS我……那我也BS你……靠)

USACO Training Page Section 3.1 Score Inflation

Score Inflation

The more points students score in our contests, the happier we here at the USACO are. We try to design our contests so that people can score as many points as possible, and would like your assistance.

We have several categories from which problems can be chosen, where a "category" is an unlimited set of contest problems which all require the same amount of time to solve and deserve the same number of points for a correct solution. Your task is write a program which tells the USACO staff how many problems from each category to include in a contest so as to maximize the total number of points in the chosen problems while keeping the total solution time within the length of the contest.

The input includes the length of the contest, M (1 <= M <= 10,000) (don't worry, you won't have to compete in the longer contests until training camp) and N, the number of problem categories, where 1 <= N <= 10,000.

Each of the subsequent N lines contains two integers describing a category: the first integer tells the number of points a problem from that category is worth (1 <= points <= 10000); the second tells the number of minutes a problem from that category takes to solve (1 <= minutes <= 10000).

Your program should determine the number of problems we should take from each category to make the highest-scoring contest solvable within the length of the contest. Remember, the number from any category can be any nonnegative integer (0, one, or many). Calculate the maximum number of possible points.

PROGRAM NAME: inflate
INPUT FORMAT
Line 1:  M, N -- contest minutes and number of problem classes  
Lines 2-N+1:  Two integers: the points and minutes for each class

SAMPLE INPUT (file inflate.in)
300 4
100 60
250 120
120 100
35 20

OUTPUT FORMAT
A single line with the maximum number of points possible given the constraints.
SAMPLE OUTPUT (file inflate.out)
605

(Take two problems from #2 and three from #4.)

 

//score inflation
#include<stdio.h>
int main(void)
{
        FILE 
*fin=fopen("inflate.in","r");
        FILE 
*fout=fopen("inflate.out","w");
         
        
long n,w,v,c;
        
long ks[10001];
        
int i,j;
        fscanf(fin,
"%d %d ",&c,&n);
        
for(i=0;i<=c;i++) ks[i]=-1;
        ks[
0]=0;
        
for(i=1;i<=n;i++){
                fscanf(fin,
"%d %d ",&v,&w);
                
for(j=w;j<=c;j++)
                        
if(ks[j-w]>=0 && ks[j]<ks[j-w]+v) ks[j]=ks[j-w]+v;
        }


        
for(i=c,j=-1;i>=0;i--if(ks[i]>j)j=ks[i];
        fprintf(fout,
"%d ",j);
        
return 0;
}

         

 

NOIP2001普及组 装箱问题

描述 Description       有一个箱子容量为v(正整数,o≤v≤20000),同时有n个物品(o≤n≤30),每个物品有一个体积 (正整数)。要求从 n 个物品中,任取若千个装入箱内,使箱子的剩余空间为最小。
              输入格式 Input Format     第一行,一个整数,表示箱子容量;
第二行,一个整数,表示有n个物品;
接下来n行,分别表示这n个物品的各自体积。               输出格式 Output Format     一个整数,表示箱子剩余空间。

//装箱问题
    #include<stdio.h>
#include
<stdbool.h>
int main(void)
{
        
int n,v;
        
int i,j;
        
int ans;
        
bool ks[20001];
        
int w[31];
        
bool found=false;
        scanf(
"%d %d ",&v,&n);
        
for(i=1;i<=n;i++) scanf("%d ",&w[i]);
        memset((
void*)ks,0,sizeof(ks));
        ks[
0]=true;
        
for(i=1;i<=n;i++)
                
for(j=v;j>=w[i];j--)
                        ks[j]
=ks[j] || ks[j-w[i]];
        
for(ans=v;!ks[ans];ans--);
        printf(
"%d ",v-ans);
        
return 0;
}

 

NOIP2006 OIBH第三次模拟赛普及组Problem 3 : maxsum 最大约数和

Problem 3 : maxsum 最大约数和
源文件名:maxsum.(pas/c/cpp)
输入文件:maxsum.in
输出文件:maxsum.out
时间限制:1秒
空间限制:64M
问题描述
选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。
输入格式
输入一个正整数S。
输出格式
输出最大的约数之和。
样例输入
11
样例输出
9
样例说明
取数字4和6,可以得到最大值(1 + 2) + (1 + 2 + 3) = 9。
数据规模...

 

#include<stdio.h>
#include
<math.h>
int main(void)
{
        
int ks[1001];
        
int s,i,j,v;
        FILE 
*fin=stdin;
        fscanf(fin,
"%d ",&s);
        
for(i=0;i<=s;i++) ks[i]=-1;
        ks[
0]=0;
        
for(i=1;i<=s;i++){
                v
=value(i);
                
for(j=i;j<=s;j++)
                        
if(ks[j-i]>=0 && ks[j]<ks[j-i]+v) ks[j]=ks[j-i]+v;
        }

        v
=-1;
        
for(i=0;i<=s;i++if(ks[i]>v) v=ks[i];
        printf(
"%d ",v);
        
return 0;
}

int value(int x)
{
        
int ans=1;
        
int i;
        
if(x==1return 0;
        
for(i=2;i<x;i++)
                
if(!(x%i)) ans+=i;
        
return ans;
}