18.4.12

背景

今天上午的dp练习赛,也就标志着我们的dp练习到此告一段落了。

T1

题面

一个吉他手准备参加一场演出。他不喜欢在演出时始终使用同一个音量,所以他决定每一首歌之前他都要改变一次音量。在演出开始之前,他已经做好了一个列表,里面写着在每首歌开始之前他想要改变的音量是多少。每一次改变音量,他可以选择调高也可以调低。
音量用一个整数描述。输入文件中给定整数beginLevel,代表吉他刚开始的音量,以及整数maxLevel,代表吉他的最大音量。音量不能小于0也不能大于maxLevel。输入文件中还给定了n个整数c_1,c_2,…,c_n,表示在第i首歌开始之前吉他手想要改变的音量是多少。
吉他手想以最大的音量演奏最后一首歌,你的任务是找到这个最大音量是多少。
输入
第一行依次为三个整数:n, beginLevel, maxlevel。
第二行依次为n个整数:c_1,c_2,…,c_n。
输出
输出演奏最后一首歌的最大音量。如果吉他手无法避免音量低于0或者高于maxLevel,输出-1。
样例1
输入
3 5 10
5 3 7
输出
10

样例2
输入
4 8 20
15 2 9 10
输出
-1
数据规模
1≤n≤50,1≤c_i≤maxLevel,1≤maxLevel≤1000, 0≤beginLevel≤maxLevel。

解析

我觉得是01背包。
但由于有增大和减小音量两种情况,所以有两种处理,如果用一维优化,那么会很麻烦,难处理后效性的问题。
f[i][j]=f[i-1][j-c[i]]||f[i-1][j+c[i]]||f[i][j];

代码

#include<bits/stdc++.h>
using namespace std;
int n,st,maxl;
int c[60]={};
bool dp[60][1010]={};
void init()
{
    scanf("%d%d%d",&n,&st,&maxl);
    for(int i=1;i<=n;++i)
      scanf("%d",&c[i]);
    dp[0][st]=1;
}
void work()
{
    for(int i=1;i<=n;++i)
      {
        for(int j=0;j<=maxl-c[i];++j)
          dp[i][j]=dp[i][j]||dp[i-1][j+c[i]];
        for(int j=c[i];j<=maxl;++j)
          dp[i][j]=dp[i][j]||dp[i-1][j-c[i]];
      }
}
void outit()
{
    for(int i=maxl;i>=0;--i) 
      if (dp[n][i]) {printf("%d",i);return;}
    printf("-1"); 
}
int main()
{
    init();
    work();
    outit();
    return 0;
}

T2

题面

Hl高中要举行一场蛋糕塔比赛。注意,不是蛋糕比赛,而是蛋糕塔比赛。
学校会提供N种不同类型的蛋糕,第i种蛋糕的高度为Hi(5 <= H_i <= T),营养价值为Vi(1 <= Vi<= 1,000,000),并且保证所有蛋糕的高度为5的整数倍,每种类型的蛋糕没有数量限制。
蛋糕塔比赛的规则就是要求按照提供的蛋糕,垒成一个高度不超过T(1 <= T <= 1,000)的蛋糕塔,并且要求这个蛋糕塔所有蛋糕的营养价值累加和最高。
因为蛋糕不是很结实,参加比赛的小x发现一个现象,如果某块蛋糕的高度超过K,那么这块蛋糕下面的所有蛋糕的高度都将被压缩为自己高度的4/5,但是营养价值不会丢失。发现这个情况后的小x很兴奋,现在他想知道,如何安排自己的蛋糕塔,能让营养价值最高。

【输入】
第一行三个整数:N,T和K
接下来N行,每行两个整数:Vi 和 Hi。
【输出】
一个整数,表示蛋糕塔最大营养价值。

【输入输出样例1】
cheese.in cheese.out
3 53 25
100 25
20 5
40 10 240
【样例解释】
有3种蛋糕,蛋糕塔的高度限制为53,高度必须超过25的蛋糕才能将其下面的蛋糕高度压缩。我们按照下面的方法来垒蛋糕:
个数 高度 价值
高 -> [1] 25 100
[2] 4 20
[3] 8 40
[3] 8 40
底 -> [3] 8 40
最上的蛋糕将下方的蛋糕都压缩为4/5.最大价值为240

【数据范围】
40% 数据保证 N<=20
100% 数据保证 N<=100 其他数据看题目描述

解析

贪心+完全背包
先不考虑压缩,进行完全背包。
因为如果有压缩情况,那么一定把那个蛋糕放在最上面时营养总值一定最大!!!
所以可以贪心,也就是每次枚举一个大于等于K的蛋糕,将他们放在最上面。取剩下容积*5/4的背包值。

代码

#include<bits/stdc++.h>
using namespace std;
int n,t,k;
int dp[1810]={},w[110]={},v[110]={};
bool p[1810]={};
void init()
{
    scanf("%d%d%d",&n,&t,&k);
    for(int i=1;i<=n;++i)
      scanf("%d%d",&w[i],&v[i]);
}
void work()
{
    for(int i=1;i<=n;++i)
      for(int j=v[i];j<=t*5/4;++j)//背包容积要到t*5/4。 
        dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
void outit()
{
    int maxx=dp[t];
    for(int i=1;i<=n;++i)
      if (v[i]>=k) maxx=max(maxx,w[i]+dp[(t-v[i])*5/4]);
    printf("%d",maxx);
}
int main()
{
    init();
    work();
    outit();
    return 0;
}

tip

别忘了容积要处理到t*5/4。
答案初值存dp[t]。
因为可能所有蛋糕都没有放。

T3

题面

格斗俱乐部是格斗爱好者的一个组织,在这里,格斗者们能通过与别的成员进行格斗来释放自己的压力与轻松自己的情绪。最近俱乐部举行了一场比赛,该比赛有N位选手参加,他们将围成一个圆圈,每一场比赛圈内任意的两位相邻的选手均可进行相互的格斗,胜利者将留在圈内进入下轮比赛而失败者则直接被送往医院(没有平局)。比赛是残酷的,最后圈内将只剩下一位选手,他将是总冠军。
  我们做个奇怪的假设,两位选手进行格斗,他们比赛的结果总是确定的。虽然俱乐部的成员们都很喜欢格斗,但是他们仍然很希望能获得总冠军。现在你通过统计已经知道了任意两位选手格斗的结果,你有责任告诉每位选手,如果赛程合适安排的话,他是否可能成为总冠军。
【输入】
数据第一行是一个整数N,(1<=N<=40),表示比赛的选手数量。
接下来给出一个N*N的“0”、“1”矩阵A(行内用空格隔开),第i行第j列为 1表示选手i能战胜选手j,否则选手j能战胜选手i。
你可以假定Aij与Aji(i≠j)均是不同的且Aii=0。比赛开始时所有选手按顺时针方向由编号1到编号N站成一个圈,初始时编号1与编号N的选手是相邻的。

【输出】
输出包含N行,每行为一个整数“0”或“1”,“1”表示第i号选手有可能成为冠军,“0”表示不可能。

【输入输出样例1】
data.in
3
0 1 1
0 0 1
0 0 0
data.out
1
0
0

解析

区间DP
类似能量项链的处理,将区间乘两倍处理。
我用的是很暴力的O(n^5)的方法。
dp开了三维[st][en][k]表示从st到en区间里编号k这个选手是否能当这个区间内的冠军。
考虑区间的合并。
就是在大区间内枚举断点,将左右两边能胜出的人进行对抗,然后能得出大区间内能胜出的人。

代码

#include<bits/stdc++.h>
using namespace std;
bool dp[100][100][50]={},a[50][50]={};
int n;
void init()
{
    int x;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
      for(int j=1;j<=n;++j)
        {
          scanf("%d",&x);
          if (x==0) a[i][j]=0;
          else a[i][j]=1;
        }
    for(int i=1;i<=2*n;++i)
      dp[i][i][(i-1)%n+1]=1;
}
void work()
{
    for(int i=2;i<=n;++i)
      for(int j=1;j<=2*n-i+1;++j)
        {
            int st=j,en=st+i-1,xx;
            for(int mid=st;mid<en;++mid)
              for(int i1=1;i1<=n;++i1)
                if (dp[st][mid][i1])
                  for(int j1=1;j1<=n;++j1)
                    if (dp[mid+1][en][j1])
                      {
                        if (a[i1][j1]) xx=i1;
                        else xx=j1;
                        dp[st][en][xx]=1;
                      }
        }
}
void outit()
{
    for(int i=1;i<=n;++i)
      {
        bool p=true;
        for(int j=1;j<=n&&p;++j)
          if (dp[j][j+n-1][i]) 
            {
              printf("1\n");
              p=false;
            }
        if (p) printf("0\n");
      }
}
int main()
{
    init();
    work();
    outit();
    return 0;
}

T4

这里写图片描述
【输入】
这里写图片描述
【输出】
这里写图片描述
【输入输出样例1】
lcs.in
xabcdafg
aafbcd
1
0 2
lcs.out
4

【输入输出样例2】
lcs.in
abcdafg
aafbcd
2
0 2
2 5
lcs.out
6

解析

还不会。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值