zoukankan      html  css  js  c++  java
  • 20190220总结 动态规划2

    感觉DP有好多东西需要补起来啊

    1.P1394 贴邮票[3]

    跳转到在线页面

    Description

    问题1:有 N 种不同面额的邮票,每种只有 1 张。问:可以选用多少张邮票贴出面额 K ?计算并输出需要邮票数目的最小值最大值

    问题2:有 N 种不同面额的邮票,每种只有 p[i] 张。问:可以选用多少张邮票贴出面额 K ?计算并输出需要邮票数目的最小值最大值

    问题3:有 N 种不同面额的邮票,每种邮票有无限多张。问:可以选用多少张邮票贴出面额 K ?计算并输出需要邮票数目的最小值最大值

    Input

    第 1 行:两个整数 N 和 K,表示有N种邮票,要贴出面额 K。 

    第 2 行:有 N 个整数,表示有N种邮票的面额 a[i]。  

    第 3 行:有 N 个整数,表示有N种邮票的数量 p[i](针对问题2)。

    Output

    第 1 行:对应问题1,两个整数,表示最小值和最大值,如果无解则输出”No answer.”。  

    第 2 行:对应问题2,两个整数,表示最小值和最大值,如果无解则输出”No answer.”。 

    第 3 行:对应问题3,两个整数,表示最小值和最大值,如果无解则输出”No answer.”。

    Sample Input

    5 12
    1 2 3 6 4
    1 2 1 2 1
    

    Sample Output

    3 4 
    2 5 
    2 12 
    

    Hint

    1 <= N <= 100; 1 <= K <= 20000; 每种邮票面额不超过200。
    

    分析

    对于T1:

    设 $ f[n][k] $ 表示用 (n) 种邮票贴出 (k) 面额的最大和最小邮票数

    要贴出 (k​) 的面额,考虑是否选择第 (n​) 种邮票:

    1. (n-1​) 种邮票已经贴出 (k​) ,不选择第 (n​)
    2. (n-1​) 种邮票已经贴出 (k-a[n]​) ,选择第 (n​)

    动态转移方程为:

    [max:f[n][k]=max(f[n-1][k],f[n-1][k-a[n]])\\ min:f[n][k]=min(f[n-1][k],f[n-1][k-a[n]]) ]

    可以用滚动数组优化到一维

    对于T2:

    邮票可以选择 (1,2,……,p[n])

    在T1基础上多一层循环

    对于T3:

    邮票可以选择 (1,2,……,frac{k}{a[n]}​)

    在T1基础上多一层循环

    Codes

    #include <bits/stdc++.h>
    #define INF 99999999
    #define maxn 101
    #define maxk 20001
    using namespace std;
    int f11[maxk],f12[maxk];
    int f21[maxk],f22[maxk];
    int f31[maxk],f32[maxk];
    int a[maxn],p[maxn],n,k;
    inline void t1()
    {
    for(int i=1;i<=k;i++)
    f11[i]=INF,f12[i]=-INF;
    for(int i=1;i<=n;i++)
    for(int j=k;j>=1;j--)
    if(j>=a[i]){
    f11[j]=min(f11[j],1+f11[j-a[i]]);
    f12[j]=max(f12[j],1+f12[j-a[i]]);
    }
    cout<<f11[k]<<' '<<f12[k];
    }
    inline void t2()
    {
    for(int i=1;i<=k;i++)
    f21[i]=INF,f22[i]=-INF;
    for(int i=1;i<=n;i++)
    for(int j=k;j>=1;j--)
    for(int h=1;h<=p[i];h++)
    if(j>=a[i]*h){
    f21[j]=min(f21[j],h+f21[j-h*a[i]]);
    f22[j]=max(f22[j],h+f22[j-h*a[i]]);
    }
    cout<<f21[k]<<' '<<f22[k];
    }
    inline void t3()
    {
    for(int i=1;i<=k;i++)
    f31[i]=INF,f32[i]=-INF;
    for(int i=1;i<=n;i++)
    for(int j=k;j>=1;j--){
    int lim=k/a[i];
    for(int h=1;h<=lim;h++)
    if(j>=a[i]*h){
    f31[j]=min(f31[j],h+f31[j-h*a[i]]);
    f32[j]=max(f32[j],h+f32[j-h*a[i]]);
    }
    }
    cout<<f31[k]<<' '<<f32[k];
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
    freopen("testin.txt","r",stdin);
    freopen("testout.txt","w",stdout);
    #endif
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    cin>>a[i];
    for(int i=1;i<=n;i++)
    cin>>p[i];
    t1(); putchar('
    ');
    t2(); putchar('
    ');
    t3();
    return 0;
    }
    

    2.ContestP2【USACO3.1.2】总分

    跳转到在线页面

    Description

      学生在我们USACO的竞赛中的得分越多我们越高兴。我们试着设计我们的竞赛以便人们能尽可能的多得分,这需要你的帮助。

      我们可以从几个种类中选取竞赛的题目,这里的一个“种类”是指一个竞赛题目的集合,解决集合中的题目需要相同多的时间并且能得到相同的分数。你的任务是写一个程序来告诉USACO的职员,应该从每一个种类中选取多少题目,使得解决题目的总耗时在竞赛规定的时间里并且总分最大。

      输入包括竞赛的时间:M,题目种类数:N和。后面的每一行将包括两个整数来描述一个"种类": 第一个整数说明解决这种题目能得的分数(1 <= points <= 10000),第二整数说明解决这种题目所需的时间(1 <= minutes <= 10000)。你的程序应该确定我们应该从每个"种类"中选多少道题目使得能在竞赛的时间中得到最大的分数。

      来自任意的"种类"的题目数目可能是任何非负数(0或更多)。

    Input

    第 1 行: M, N--竞赛的时间和题目"种类"的数目。   第 2-N+1 行: 两个整数:每个"种类"题目的分数和耗时。

    Output

    单独的一行包括那个在给定的限制里可能得到的最大的分数。

    Sample Input

    300 4
    100 60
    250 120
    120 100
    35 20
    

    Sample Output

    605
    

    Hint

    1 <= M <= 10,000 1 <= N <= 10,000 种题目的分数在1..10000范围内;解答所需时间在1..10000范围内
    

    分析

    一个完全背包问题,需要用压缩空间优化

    (虽然不优化也只有95MB)

    详见:P1395:背包问题[3]

    Codes

    #include <bits/stdc++.h>
    #define INF 2147483647
    #define maxn 10001
    using namespace std;
    int s[maxn],t[maxn];
    int f[maxn];
    int n,m;
    inline void dp()
    {
    for(int i=1;i<=m;i++)
    for(int j=1;j<=n;j++){
    if(j>=t[i])
    f[j]=max(f[j],f[j-t[i]]+s[i]);
    }
    cout<<f[n];
    } 
    int main()
    {
    #ifndef ONLINE_JUDGE
    freopen("testin.txt","r",stdin);
    freopen("testout.txt","w",stdout);
    #endif
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    cin>>s[i]>>t[i];
    dp();
    return 0;
    }
    

    3.ContestP4【训练题】最大约数和

    跳转到在线页面

    Description

    选取和不超过 S 的若干不同正整数,使得所有数的约数(不含它本身)和为最大!

    Input

    一个整数 S 。

    Output

    输出最大的约数之和。

    Sample Input

    11
    

    Sample Output

    9
    

    Hint

    对于30%的数据:S<=10
    对于100%的数据:S<=1000
    

    分析

    一个 (infty​) 背包问题,要先预处理 ([;1,S;]​) 中的所有数的约数和(除开它本身,但是有 (1​) )

    然后,(v=n,nin [1,S]​)(p=Sigma_{i=1}^n(nmod i=0)​),看做 $ infty ​$ 背包问题求解

    Codes

    #include <bits/stdc++.h>
    #define maxn 1001
    using namespace std;
    int p[maxn];
    inline void init(int LIM)
    { // 预处理 P 数组 
    for(int i=2;i<=LIM;i++){
    int sq=sqrt(i);
    for(int j=2;j<=sq;j++){
    if(i%j==0){
    p[i]+=(j+i/j);
    if(i/j==j) p[i]-=j;
    }
    }
    p[i]++;
    }
    }
    inline void dp(int LIM)
    {
    for(int i=1;i<=LIM;i++){
    for(int j=1;j<=i;j++)
    p[i]=max(p[i],p[i-j]+p[j]);
    }
    cout<<p[LIM];
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
    freopen("testin.txt","r",stdin);
    freopen("testout.txt","w",stdout);
    #endif
    int s;
    cin>>s;
    init(s);
    dp(s);
    return 0;
    }
    

    4.ContestP8【训练题】多背包问题

    跳转到在线页面

    Description

    有 N 种物品,每种只有一个,第 i 种物品的体积为 vi,重量为 wi。

    现有两个背包,容量分别为为 C1 和 C2 的背包,问如何选择装入两个背包的物品,使得装入两个背包的物品的总价值尽量大。

    Input

    第一行三个整数:C1、C2和N。  

    接下来的N行,每行两个整数,第i+1行的两个整数分别表示第i个物体的体积和重量。。

    Output

    包含一行一个整数,表示背包所装物体的最大重量。

    Sample Input

    8 6 5
    4 3
    2 1
    3 2
    3 4
    5 6
    

    Sample Output

    14
    

    Hint

    1<=N<=100;  
    1<=vi<=2000;  
    1<=c1,c2<=2000;  
    1<=wi<=10000;
    

    分析

    一个变形的 $ 0/1 $ 背包问题,在普通的倒推求解基础上要增开一维,即:

    [f[V_1][V_2]={egin{cases} max(f[V_1][V_2],f[V_1-v[i]][V_2])(V_1geq v[i])\\ max(f[V_1][V_2],f[V_1][V_2-v[i]])(V_2geq v[i]) end{cases}}left(V_1in [0,C_1],(V_2in [0,C_2] ight) ]

    满足其一,执行其一,同时满足,同时执行

    Codes

    #include <bits/stdc++.h>
    #define maxc 2001
    #define maxn 101
    using namespace std;
    int f[maxc][maxc];
    int c1,c2,n;
    int v[maxn],w[maxn];
    inline void dp()
    {
    for(int i=1;i<=n;i++)
    for(int v1=c1;v1>=0;v1--)
    for(int v2=c2;v2>=0;v2--){
    if(v1>=v[i]) 
    f[v1][v2]=max(f[v1][v2],w[i]+f[v1-v[i]][v2]);
    if(v2>=v[i])
    f[v1][v2]=max(f[v1][v2],w[i]+f[v1][v2-v[i]]);
    }
    cout<<f[c1][c2];
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
    freopen("testin.txt","r",stdin);
    freopen("testout.txt","w",stdout);
    #endif
    cin>>c1>>c2>>n;
    for(int i=1;i<=n;i++)
    cin>>v[i]>>w[i];
    dp();
    return 0;
    }
    

    5.ContestP11【训练题】潜水员

    跳转到在线页面

    Description

    潜水员为了潜水要使用特殊的装备。他有一个带2种气体的气缸:一个为氧气,一个为氮气。让潜水员下潜的深度需要各种的数量的氧和氮。

    潜水员有一定数量的气缸。每个气缸都有重量和气体容量。潜水员为了完成他的工作需要特定数量的氧和氮。他完成工作所需气缸的总重的最低限度的是多少?

    例如:潜水员有5个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:

    3 36 120
    10 25 129
    5 50 250
    1 45 130
    4 20 119

    如果潜水员需要5升的氧和60升的氮则总重最小为249(1,2或者4,5号气缸)。你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。

    Input

    第一行有2整数t,a(1<=t<=21,1<=a<=79)。它们表示氧,氮各自需要的量。

    第二行为整数n(1<=n<=1000)表示气缸的个数。此后的n行,每行包括ti,ai,wi(1<=ti<=21,1<=ai<=79,1<=wi<=800)3整数。这些各自是:第i个气缸里的氧和氮的容量及汽缸重量。

    Output

    仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。

    Sample Input

    5 60
    5
    3 36 120
    10 25 129
    5 50 250
    1 45 130
    4 20 119
    

    Sample Output

    249
    

    Hint

    No Hint
    

    分析

    本质还是 $ 0/1 $ 背包问题,限制条件从单一元增加为二元,那么数组的维数和 (for) 循环的层数也增加

    要注意当前的氧气,氮气总量超过要求量是也是合法的,所以最好是用加法由小往大推

    也即是:

    [f[GT][GA]=min(f[GT][GA],f[gt][ga]+w[i])\\其中:\\ GT=egin{cases}T(gt+t[i]>T)\\gt+t[i](gt+t[i]leq T) end{cases},GA=egin{cases}T(ga+a[i]>T)\\ga+a[i](ga+a[i]leq T) end{cases} ]

    Codes

    #include <bits/stdc++.h>
    #define maxc 2001
    #define maxn 101
    using namespace std;
    int f[maxc][maxc];
    int c1,c2,n;
    int v[maxn],w[maxn];
    inline void dp()
    {
    for(int i=1;i<=n;i++)
    for(int v1=c1;v1>=0;v1--)
    for(int v2=c2;v2>=0;v2--){
    if(v1>=v[i]) 
    f[v1][v2]=max(f[v1][v2],w[i]+f[v1-v[i]][v2]);
    if(v2>=v[i])
    f[v1][v2]=max(f[v1][v2],w[i]+f[v1][v2-v[i]]);
    }
    cout<<f[c1][c2];
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
    freopen("testin.txt","r",stdin);
    freopen("testout.txt","w",stdout);
    #endif
    cin>>c1>>c2>>n;
    for(int i=1;i<=n;i++)
    cin>>v[i]>>w[i];
    dp();
    return 0;
    }
    

    6.ontestP19【专题训练】采药

    跳转到在线页面

    Description

    辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。

    医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一些询问,在给定的一段时间T内里,你可以采到草药的总价值最大是多少。”

    如果你是辰辰,你能完成这个任务吗?

    Input

    第一行有两个整数N(1 <= N <= 1000)和M(1 <= M <= 100),用一个空格隔开,N代表医师有N个询问,M代表山洞里的草药的数目。

    接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

    在接下来的N行,每行一个整数T(1 <= T <= 1000),表示医师询问的时间长度。

    Output

    每个询问输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

    Sample Input

    3 3
    71 100
    69 1
    1 2
    70
    71
    72
    

    Sample Output

    3
    100
    102
    

    Hint

    对于30%的数据,M <= 10;
    对于全部的数据,M <= 100。
    

    分析

    一个 (0/1) 背包问题,要用倒推进行空间+时间优化

    Codes

    #include <bits/stdc++.h>
    #define maxn 1001
    using namespace std;
    int t[maxn],p[maxn];
    int f[maxn];
    int n,m;
    inline void dp(int lim)
    {
    memset(f,0,sizeof(f));
    for(int i=1;i<=m;i++)
    for(int j=lim;j>=0;j--)
    if(j>=t[i]) f[j]=max(f[j],f[j-t[i]]+p[i]);
    cout<<f[lim];
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
    freopen("testin.txt","r",stdin);
    freopen("testout.txt","w",stdout);
    #endif
    cin>>n>>m; int TIME;
    for(int i=1;i<=m;i++)
    cin>>t[i]>>p[i];
    for(int i=1;i<=n;i++){
    cin>>TIME;
    dp(TIME); putchar('
    ');
    }
    return 0;
    }
    

    7.ContestP20【训练题】逃亡的准备

    跳转到在线页面

    Description

    在《Harry Potter and the Deathly Hallows》中,Harry Potter他们一起逃亡,现在有许多的东西要放到赫敏的包里面,但是包的大小有限,所以我们只能够在里面放入非常重要的物品。

    现在给出该种物品的数量、体积、价值的数值,希望你能够算出怎样能使背包的价值最大的组合方式,并且输出这个数值,赫敏会非常地感谢你。

    Input

    第一行有2个整数,物品种数n和背包装载体积v。  第 2 行到 n+1 行每行3个整数,为第i种物品的数量m、体积w、价值s。

    Output

    仅包含一个整数,即为能拿到的最大的物品价值总和。

    Sample Input

    2 10
    3 4 3
    2 2 5
    

    Sample Output

    13
    

    Hint

    对于30%的数据:1<=v<=500、1<=n<=2000、1<=m<=10、1<=w<=20、1<=s<=100
    对于100%的数据:1<=v<=500、1<=n<=2000、1<=m<=5000、1<=w<=20、1<=s<=100
    

    分析

    一个多重背包问题,但是用倒推的方法会超时4个点……

    于是 (Perisino​) 毅然在 (if(jgeq t[i] imes k);……​) 的后面加上了 (else;break​),大胆而合理地提交了代码

    然后就AC了!

    这是一种新的时间优化方法吗?!(我们也不知道)

    Codes

    #include <bits/stdc++.h>
    #define INF 2147483647
    #define maxn 2001
    using namespace std;
    int s[maxn],t[maxn],num[maxn];
    int f[maxn];
    int n,m;
    inline void dp()
    {
    for(int i=1;i<=n;i++)
    for(int j=m;j>=1;j--){
    for(int k=1;k<=num[i];k++)
    if(j>=t[i]*k)
    f[j]=max(f[j],f[j-k*t[i]]+k*s[i]);
    else break;// magical codes
    }
    cout<<f[m];
    } 
    int main()
    {
    #ifndef ONLINE_JUDGE
    freopen("testin.txt","r",stdin);
    freopen("testout.txt","w",stdout);
    #endif
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    cin>>num[i]>>t[i]>>s[i];
    dp();
    return 0;
    }
    

    8.ContestP21 劲歌金曲

    跳转到在线页面

    Description

    如果问一个麦霸:“你在KTV里必唱的曲目有那些?”得到的回答通常包含一首“神曲”――古巨基的《劲歌经典》。为什么呢?一般说来,KTV不会在“时间到”的时候鲁莽地把正在唱的歌切掉,而是会等它放完。例如,在还有15秒时再唱一首2分钟的歌,则实际上多唱了105秒。但是融合了37首歌曲的《劲歌经典》长达11分18秒,如果唱这首歌,相当于多唱了663秒。

    假定你正在唱KTV,还剩下t秒时间。你决定下来只唱你最爱的n首歌(不含《劲歌经典》)中的一些,在时间结束之前再唱一个《劲歌金曲》,使得唱的总曲目尽量多(包含《劲歌经典》),在此前提下尽量晚的离开KTV。

    Input

    第一行为在整数T,表示测试数据组数。  

    每组测试数据占两行,第一行为整数n和t,分别表示n首歌曲和你还剩下的时间为t秒。

    第二行为n个整数,分别表示n首的时长(保证不超过3分钟)。  

    输入保证所有n+1首歌曲的总长度严格大于t。

    Output

    每组数据输出一行,包含两个整数,分别表示你还能唱的曲目的最多数目和最大的总时长。

    Sample Input

    2
    3 100
    60 70 80
    3 100
    30 69 70
    

    Sample Output

    2 758
    3 777
    

    Hint

    T<=100
    1<=n<=50
    1<=t<=1 000 000 000
    

    分析

    首先不要被 (1leq tleq 1,000,000,000​) 吓到, 题中提及每首歌市场不超过3分钟(180秒),加上 (1leq nleq 50​)

    也就是 (1leq tleq 9,000) ,用 $ 0/1 $ 背包+倒推优化做

    这里注意倒推范围是 ([1,T-1]) ,因为你要把最后一秒留给《劲歌金曲》来唱最划算

    **在推完之后要回去再扫一遍看有没有比 (f[T]​) 更大的,若有,更新 (ans=T-1 o ans=new\_max​) **

    最后的时长为 $ ans+678(11 imes 60+18) $

    Codes

    #include <bits/stdc++.h>
    #define maxT 10001
    #define maxn 51
    #define oo 0x3f
    using namespace std;
    int f[maxT],t[maxn];
    int n,T,tot;
    inline void dp()
    {
    memset(f,-oo,sizeof(f));
    f[0]=0;
    for(int i=1;i<=n;i++)
    for(int j=tot-1;j>=0;j--){
    if(j>=t[i])
    f[j]=max(f[j],f[j-t[i]]+1);
    }
    int ans=tot-1;
    for(int i=tot-1;i>=0;i--)if(f[i]>f[ans])ans=i;
    cout<<f[ans]+1<<" "<<ans+678;
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
    freopen("testin.txt","r",stdin);
    freopen("testout.txt","w",stdout);
    #endif
    cin>>T;
    while(T--){
    memset(t,0,sizeof(t));
    cin>>n>>tot;
    for(int i=1;i<=n;i++)	
    cin>>t[i];
    dp(); putchar('
    ');
    }
    return 0;
    }
    

  • 相关阅读:
    springboot~使用docker构建gradle项目
    CH BR8(小学生在上课-逆元和互质数一一对应关系)
    UNIX环境高级编程第二版代码笔记
    【Linux学习笔记】用nc实现两台主机间的文件传输(不需要输密码)
    hdu 1159
    轻量级的原型设计工具-Axure RP
    在Ubuntu 12.10 上安装部署Openstack
    [Android 中级]Voip之CSipSimple类库的编绎
    OpenStack云计算快速入门之一:OpenStack及其构成简介
    OpenStack云计算快速入门之二:OpenStack安装与配置
  • 原文地址:https://www.cnblogs.com/Ewiges-Leben/p/10409037.html
Copyright © 2011-2022 走看看