2018/4/12 DP练习赛

放假回来又是一次考试,啦啦啦~
然后发现我的DP是真的烂
。。。

T1

问题描述

一个吉他手准备参加一场演出。他不喜欢在演出时始终使用同一个音量,所以他决定每一首歌之前他都要改变一次音量。在演出开始之前,他已经做好了一个列表,里面写着在每首歌开始之前他想要改变的音量是多少。每一次改变音量,他可以选择调高也可以调低。
音量用一个整数描述。输入文件中给定整数beginLevel,代表吉他刚开始的音量,以及整数maxLevel,代表吉他的最大音量。音量不能小于0也不能大于maxLevel。输入文件中还给定了n个整数 c1,c2,,cn ,表示在第i首歌开始之前吉他手想要改变的音量是多少。
吉他手想以最大的音量演奏最后一首歌,你的任务是找到这个最大音量是多少。
输入

第一行依次为三个整数:n, beginLevel, maxlevel
第二行依次为n个整数: c1,c2,,cn
输出

输出演奏最后一首歌的最大音量。如果吉他手无法避免音量低于0或者高于maxLevel,输出-1
数据规模

1n501cimaxLevel1maxLevel10000beginLevelmaxLevel

唔。。。
全世界都认为这是一道水题,递推乱搞
然后我**就写出了一个带有后效性的DP
其实应该设f[i][j]为第i首歌能否取到j音量
所以f[i][j]为1的条件是

a[i]jf[i1][ja[i]]==1j+a[i]maxLevelf[i1][j+a[i]]

那么这道题目就很简单啦
代码如下:

#include<bits/stdc++.h>
using namespace std;
int n;
int bl,ml;
int a[55];
int f[55][1001];
int main()
{
    scanf("%d%d%d",&n,&bl,&ml);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    f[0][bl]=1;
    for (int i=1;i<=n;i++)
    {
        for (int j=ml;j>=0;j--)
        {
            if (j>=a[i] && f[i-1][j-a[i]]==1)
                f[i][j]=1;
            if (j+a[i]<=ml && f[i-1][j+a[i]]==1)
                f[i][j]=1;
        }
    }
    for (int i=ml;i>=1;i--)
        if (f[n][i]==1)
        {
            cout <<i <<endl;
            return 0;
        }
    cout <<-1 <<endl;
    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。
输出

一个整数,表示蛋糕塔最大营养价值。

数据范围

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

这题就是完全背包的板子,只是题目中的压缩让我们感到不好处理
我们可以先直接做完全背包,把每个蛋糕的H都当作压缩过做
这里要注意:枚举高度时上界就变成了t*5/4
然后再枚举一遍每个蛋糕,如果h>=k了就处理一下
具体看代码:

#include<bits/stdc++.h>
using namespace std;
int n;
int k;
struct ck{
    int h;
    int v;
}a[101];
int f[1300];
int t;
int main()
{
    scanf("%d%d%d",&n,&t,&k);
    for (int i=1;i<=n;i++)
        scanf("%d%d",&a[i].v,&a[i].h);
    for (int i=1;i<=n;i++)
    {
        for (int j=0;j<=t*1.25;j++)//注意上界
        {
            if (j>=a[i].h)
                f[j]=max(f[j],f[j-a[i].h]+a[i].v);
        }
    }
    int maxx=f[t];
    for (int i=1;i<=n;i++)
        if (a[i].h>=k)
            maxx=max(maxx,f[(int)((t-a[i].h)*1.25)]+a[i].v);//虚伪处理
    cout <<maxx <<endl;
    return 0;
}

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”表示不可能。

首先,题目里说人是一个环,所以在预处理时可以把a[i+n][j+n]赋值为a[i][j]来解决这个问题
我们设f[i][j]=1为i,j最终会打一场比赛,也就是说i和j可以击败从i+1到j-1的所有人
那么当取i+1到j-1中的任意一个k时
只有满足
(a[i][k]==1 || a[j][k]==1) && f[i][k]==1 && f[k][j]==1
时,f[i][j]才能标为1
所以如果f[i][i+n]==1,那么i就有可能获胜
最后只要输出f[i][i+n]的值就行了
代码如下:

#include<bits/stdc++.h>
using namespace std;
int n;
int a[111][111];
bool f[111][111];
int main()
{
    cin >>n;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
        {
            cin >>a[i][j];
            a[i][j+n]=a[i][j];
            a[i+n][j]=a[i][j];
            a[i+n][j+n]=a[i][j];
        }
    n*=2;
    for (int i=1;i<=n;i++)
        f[i][i+1]=1;
    for (int i=n-2;i>=1;i--)
    {
        for (int j=i+2;j<=n;j++)
        {
            for (int l=i+1;l<=j-1;l++)
                if ((a[i][l]==1 || a[j][l]==1) && f[i][l]==1 && f[l][j]==1)
                    f[i][j]=1;
        }
    }
    for (int i=1;i<=n/2;i++)
        if (f[i][i+n/2]==1)
            cout <<1 <<endl;
        else
            cout <<0 <<endl;
    return 0;
}

T4

问题描述

这里写图片描述
输入

这里写图片描述
输出

这里写图片描述

数据范围

这里写图片描述

这题不会。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值