智力竞赛(慢慢领悟dp)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jxust_tj/article/details/52389721

#1285 : 智力竞赛

时间限制: 5000ms
单点时限: 1000ms
内存限制: 256MB

描述

小Hi、小Ho还有被小Hi强拉来的小Z,准备组队参加一个智力竞赛。竞赛采用过关制,共计N个关卡。在第i个关卡中,小Hi他们需要获得Ai点分数才能够进入下一关。每一关的分数都是独立计算的,即使在一关当中获得超过需要的分数,也不会对后面的关卡产生影响。

小Hi他们可以通过答题获得分数。答对一道题获得S点分数,答错一道题获得T点分数。在所有的N个关卡中,小Hi他们一共有M次答题机会。在每个关卡中,都可以在累计答题次数不超过M的情况下使用任意次的答题机会。

那么现在问题来了,对于给定的N、M、S、T和A,小Hi他们至少需要答对多少道题目才能够完成所有的关卡呢?

输入

每个输入文件包含多组测试数据,在每个输入文件的第一行为一个整数Q,表示测试数据的组数。

每组测试数据的第一行为四个正整数N、M、S和T,意义如前文所述。

第二行为N个正整数,分别表示A1~AN

对于40%的数据,满足1<=N,M<=100

对于100%的数据,满足1<=N,M<=1000,1<=T<S<=10,1<=Ai<=50

对于100%的数据,满足1<=Q<=100

输出

对于每组测试数据,如果小Hi他们能够顺利完成关卡,则输出一个整数Ans,表示小Hi他们至少需要答对的题目数量,否则输出No。

样例输入
1
2 10 9 1
12 35 
样例输出
5

题目链接:http://hihocoder.com/problemset/problem/1285

分析:

此题花了很长时间去思考如何定义状态。首先 “完成了多少个关卡” 这个状态是要有的,这样对于一个新的关卡可以看与前一个关卡之间的关系,从而推到完成n个关卡时的状态。然后 “总共用了多少次机会” 这个状态是否需要呢,不知道,但这个变量是条件中的一个限制,肯定要用,先试试看吧,看是否能够利用它好推出前后的状态关系。“完成当前关卡用了几次机会” 这个状态不需要,因为它的前一个状态与当前状态没有太大关系,状态不好递推。同理,“当前关卡答对几次” 、“总共答对几次” 等等状态的前一个状态与当前状态没有太大关系,不好推,所以不要。发现要定义的状态一般都是题目给的一些限制变量,而且都是些累积类状态。比如说 “总共用了多少次机会” 这个状态的值是需要每个关卡用的机会加起来的,我称之为累积类状态。而假如要用非累积类状态,比如说 “完成当前关卡用了几次机会” 这种不用累加的状态,一般都是因为此种状态的前一个状态和后一个状态有某种限制或可能产生特殊关系。

如果状态定义好了,那么就成功一大半了。接下来就是写状态转移方程。顾名思义,状态转移方程就是看状态是如何转移的(不是装逼==。是我现在的确对这个有点感觉了)。一般就是看某个状态可能从哪些状态转移(这些状态要离那个状态最近,也就是中间不经过其他状态)而来,然后得出状态之间的关系写出相应方程。

P.s 不要看我分析得好像好厉害的样子,但是我其实花了好长时间才想出来的快哭了。也算刚开始对动态规划有点领悟了吧。

code:

#include<bits/stdc++.h>
using namespace std;
#define INF 1<<30
#define F first
#define S second
typedef long long ll;
const int N = 1010;
int dp[N][N]; //dp[i][j]表示花费j次机会完成i个关卡时已至少答对的题数
int main()
{
    int Q, n, m, s, t, a[N];

    scanf("%d", &Q);
    while(Q--)
    {
        scanf("%d%d%d%d", &n,&m,&s,&t);
        for(int i=1; i<=n; i++)
            scanf("%d", a+i);
        for(int i=0; i<=n; i++)
            for(int j=0; j<=m; j++)
                dp[i][j] = INF;
        dp[0][0] = 0;
        for(int i=1; i<=n; i++)
        {
            for(int j=0; j<=m; j++)
            {
                for(int x=0; x<=(a[i]/s+(a[i]%s!=0)) && x<=j; x++)
                {//完成当前关卡对了x个题目
                    int y = (a[i]-x*s)/t+((a[i]-x*s)%t!=0); 
                    if(a[i] <= x*s) y = 0; //完成当前关卡错了y个题目
                    if(j-x-y>=0) dp[i][j] = min(dp[i][j],dp[i-1][j-x-y]+x);
                    //printf("x=%d y=%d dp[%d][%d]=%d\n", x,y,i,j,dp[i][j]);
                }
            }
        }
        int res = INF;
        for(int j=0; j<=m; j++)
            res = min(res,dp[n][j]);
        if(res == INF) puts("No");
        else printf("%d\n", res);
    }
    return 0;
}




猜你喜欢

转载自blog.csdn.net/jxust_tj/article/details/52389721
今日推荐