DP

D-猪钱罐

题目链接:https://vjudge.net/contest/279505#problem/D
在 ACM 能够开展之前,必须准备预算,并获得必要的财力支持。该活动的主要收入来自于 Irreversibly Bound Money (IBM)。思路很简单。任何时候,某位 ACM 会员有少量的钱时,他将所有的硬币投入到小猪储钱罐中。这个过程不可逆,因为只有把小猪储钱罐打碎才能取出硬币。在足够长的时间之后,小猪储钱罐中有了足够的现金,用于支付 ACM 活动所需的花费。

但是,小猪储钱罐存在一个大的问题,即无法确定其中有多少钱。因此,我们可能在打碎小猪储钱罐之后,发现里面的钱不够。显然,我们希望避免这种不愉快的情况。唯一的可能是,称一下小猪储钱罐的重量,并尝试猜测里面的有多少硬币。假定我们能够精确判断小猪储钱罐的重量,并且我们也知道给定币种的所有硬币的重量。那么,我们可以保证小猪储钱罐中最少有多少钱。

你的任务是找出最差的情形,即判断小猪储钱罐中的硬币最少有多少钱。我们需要你的帮助。不能再贸然打碎小猪储钱罐了!

输入

输入包含 T 组测试数据。输入文件的第一行,给出了 T 的值。

对于每组测试数据,第一行包含 E 和 F 两个整数,它们表示空的小猪储钱罐的重量,以及装有硬币的小猪储钱罐的重量。两个重量的计量单位都是 g (克)。小猪储钱罐的重量不会超过 10 kg (千克),即 1 <= E <= F <= 10000 。每组测试数据的第二行,有一个整数 N (1 <= N <= 500),提供了给定币种的不同硬币有多少种。接下来的 N 行,每行指定一种硬币类型,每行包含两个整数 P 和 W (1 <= P <= 50000,1 <= W <=10000)。P 是硬币的金额 (货币计量单位);W 是它的重量,以 g (克) 为计量单位。

输出

对于每组测试数据,打印一行输出。每行必须包含句子 “The minimum amount of money in the piggy-bank is X.” 其中,X 表示对于给定总重量的硬币,所能得到的最少金额。如果无法恰好得到给定的重量,则打印一行 “This is impossible.” 。

题目大意:

称一下小猪储钱罐的重量,在存在n种硬币满足以上,判断小猪储钱罐中的硬币最少有多少钱。如果不存在n种硬币满足以上猪钱罐重量的情况,则就输出This is impossible.。

题目思路:

同样也是一道01背包问题,做法和第一题差不多。设dp[n][n],第一个n是选择物品的个数,第二个n是总物品的重量,dp是所选择的总物品的价值。列出状态方程: dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+value[i]);唯一不同的是最后选择的是个n为F-E(猪钱罐的容量)。

代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define INF 1000000000
using namespace std;

int E,F,N;
int w[505],v[505],dp[505][10005];

void solve(){
  for(int i=1;i<=N;i++){
    for(int j=0;j<=F-E;j++){
        if(j<w[i]) dp[i][j]=dp[i-1][j];
        else{
            dp[i][j]=min(dp[i-1][j],dp[i][j-w[i]]+v[i]);
        }
    }
  }
  if(dp[N][F-E]!=INF) cout<<"The minimum amount of money in the piggy-bank is "<<dp[N][F-E]<<"."<<endl;
  else cout<<"This is impossible."<<endl;
}

int main(){
 int T;
 cin>>T;
 while(T--){
for(int i=0;i<504;i++){
    for(int j=0;j<10004;j++){
        dp[i][j]=INF;
    }
}
       dp[0][0]=0;
    cin>>E>>F;
    cin>>N;
    v[0]=0;
    w[0]=0;
    for(int i=1;i<=N;i++){
        cin>>v[i]>>w[i];
    }
    solve();
 }
}

B-CD

题目链接:https://vjudge.net/contest/279505#problem/B
You have a long drive by car ahead. You have a tape recorder, but unfortunately your best music is on CDs. You need to have it on tapes so the problem to solve is: you have a tape N minutes long. How to choose tracks from CD to get most out of tape space and have as short unused space as possible.

Assumptions:

number of tracks on the CD. does not exceed 20
no track is longer than N minutes
tracks do not repeat
length of each track is expressed as an integer number
N is also integer
Program should find the set of tracks which fills the tape best and print it in the same sequence as the tracks are stored on the CD

Input

Any number of lines. Each one contains value N, (after space) number of tracks M and durations of the tracks. For example from first line in sample data: N=5, M=3, first track lasts for 1 minute, second one 3 minutes, next one 4 minutes

The input data satisfies the following constraints:

N≤10000
M≤20

Output

Set of tracks (and durations) which are the correct solutions and string ``sum:" and sum of duration times.

Sample Input

5 3 1 3 4
10 4 9 8 4 2
20 4 10 5 7 4
90 8 10 23 1 2 3 4 5 7
45 8 4 10 44 43 12 9 8 2

Sample Output

1 4 sum:5
8 2 sum:10
10 5 4 sum:19
10 23 1 2 3 4 5 7 sum:55
4 10 12 9 8 2 sum:45

题目大意:

磁带有N分钟长。如何从CD中选择曲目,以充分利用磁带空间并尽可能短地保留未使用的空间。

题目思路:

这道题也是一道01背包问题,只是在求最大CD轨道分钟数上加上记录选择轨道的相应的分钟数,因此我们做这道题的时候,不能像上一题一样用一个一维去进行DP,我们要特别开多一个数组来储存我们选择的轨道编号。

代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

int N,M;
int n[25];
int dp[25][10000],id[25],path[25][10000];

void solve(){
  for(int i=0;i<M;i++){
    for(int j=0;j<=N;j++){
        if(j<n[i]){
            dp[i+1][j]=dp[i][j];
        }
        else{
            if(dp[i][j]<dp[i][j-n[i]]+n[i]){
                dp[i+1][j]=dp[i][j-n[i]]+n[i];
                path[i+1][j]=1;
            }
            else dp[i+1][j]=dp[i][j];
        }
    }
  }
}

int main(){
  while(cin>>N>>M){
        memset(dp,0,sizeof(dp));
        memset(path,0,sizeof(path));
  for(int i=0;i<M;i++){
    cin>>n[i];
    }
    solve();
    int k=N;
    for(int i=M;i>=1;i--){
        if(path[i][k]==1&&k>=0){
            cout<<n[i-1]<<" ";
            k=k-n[i-1];
        }
    }
  cout<<"sum:"<<dp[M][N]<<endl;
}
}

E-划分

题目链接:https://vjudge.net/contest/279505#problem/E
现有面值为1、2、3、4、5、6的硬币若干枚,现在需要知道能不能将这些硬币分成等额的两堆。

Input

每行输入6个正整数,分别表是面值为1、2、3、4、5、6的硬币的个数,若输入6个0代表输入结束。单种硬币的数量不会超过20000。

Output

若能分割,输出 `Can be divided.’’,若不能输出``Can’t be divided.’’

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.

题目思路:

完全背包,先计算总价值,然后取一半,就判断能否凑出这个一半的值即可。构建状态方程dp[j]=max(dp[j],dp[j-ik]+ik)。然后判断大盘[eve]

代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

int a[10],eve,sum;
int dp[120000+10];
int solve(){
  for(int i=1;i<=6;i++){
    int num=min(a[i],eve/i);
    for(int k=1;num>0;k*=2){
        if(k>num) k=num;
        num-=k;
        for(int j=eve;j>=i*k;j--)
            dp[j]=max(dp[j],dp[j-i*k]+i*k);
    }
  }
  return dp[eve];
}

int main(){
  for(int T=1;;T++){
        memset(dp,0,sizeof(dp));
        sum=0;
   for(int i=1;i<=6;i++) {cin>>a[i];sum+=a[i];}
  if(sum==0) break;
    if((a[1]+a[2]*2+a[3]*3+a[4]*4+a[5]*5+a[6]*6)%2==1){
        printf("Collection #%d:\nCan't be divided.\n\n",T);
    }
    else{
        eve=(a[1]+a[2]*2+a[3]*3+a[4]*4+a[5]*5+a[6]*6)/2;
       int ans=solve();
       if(ans==eve) printf("Collection #%d:\nCan be divided.\n\n",T);
       else printf("Collection #%d:\nCan't be divided.\n\n",T);
    }
  }
  return 0;
}

A-送快递

题目链接: https://vjudge.net/contest/279505#problem/A

现在我们有N个配件,他们有不同的价值. 但是我们背包的容量是有限的,因为我们只有一个一级包, 所以我们最多可以装V重量的东西. 但是为了能更好的吃到鸡(不存在的)我们要携带更有价值的配件,请问我们最多能拿多少价值的配件来当快递员呢??

Input

输入的第一行是T, 表示有一共要打T场比赛.
每组数据由三行组成.
第一行包含两个整数N和V(N <= 1000, V <= 1000). N表示配件的个数, V表示一级包的大小(系统会更新嘛).
第二行包含N个整数, 表示每一个配件的价值.
第三行包含N个整数, 表示每个配件的重量.

Output

对每一组数据, 输出我们最多能拿多少价值的配件.

Sample Input

1
10 10
1 3 5 7 9 11 13 15 17 19
19 17 15 13 11 9 7 5 3 1

Sample Output

51

题目大意:

有一个容量为V的背包,然后有n件物品(有各自的重量和价值)进行选择,问怎样在背包的容量的范围内,使背包内物品的总价值最大。

题目分析:

这是一个经典的01背包问题,设dp[n][n],第一个n是选择物品的个数,第二个n是总物品的重量,dp是所选择的总物品的价值。列出状态方程: dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+value[i]);在背包容量逐渐增加的情况下取比较不取的最优解。

代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;

int N,V,dp[1005][1005];
int w[1005],value[1005];

void solve(){
    memset(dp,0,sizeof(dp));
  for(int i=1;i<=N;i++){
    for(int j=0;j<=V;j++){
        if(j<w[i]) dp[i][j]=dp[i-1][j];
        else
        dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+value[i]);
    }
  }
  printf("%d\n",dp[N][V]);
}

int main(){
  int T;
  cin>>T;
  while(T--){
    cin>>N>>V;
    value[0]=0;
    w[0]=0;
    for(int i=1;i<=N;i++){
        cin>>value[i];
}
    for(int j=1;j<=N;j++){
        cin>>w[j];
    }
    solve();
}
  return 0;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值