牛客练习赛36B题

https://ac.nowcoder.com/acm/contest/328/B

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述

Rabbit大学毕业后找到了一份实习工作,如果实习通过她就转正了。
实习期共有N天,其中有几天公司集体放假,Rabbit不用上班,剩下时间她可以选择工作或者休息。Rabbit工作总是越来越累,可是每当她休息时,她就重新充满了能量。简而言之,Rabbit第一天工作时一天会消耗体力1,连续第二天工作时一天会消耗体力2,连续第三题工作时一天会消耗体力3,以此类推......每当她休息后,工作的第一天又会消耗体力1。
为了让boss满意,Rabbit想工作尽量多的天数,但是懒惰的Rabbit又想让自己的总体力消耗不超过K。 

输入描述:

第一行两个整数N,K。

第二行一个长度为N的01字符串。如果第i个字符为‘1’,表示这一天Rabbit可以选择工作或者休息,否则这一天Rabbit放假。

输出描述:

输出Rabbit最多能工作的天数。

示例1

输入

复制

4 2
1011

输出

复制

2

说明

第三天和第四天里面休息一天即可,总体力消耗为2

备注:

1<=N<=400

1<=K<=N∗(N+1)/2

       

用了两个二维数组,dp[410][410],cost[410][410]。

dp[i][j]存储的是 :从前i天中抉择,且第i天处于连续工作中的第j天的情况下能取得的最大工作天数,比如dp[6][3]表示第4,5,6天是处于连续工作的,所以第6天是处于连续工作时间段的第3天,数组表示成了dp[6][3],也就是说,dp[i][j]中的倒着数的连续j天是工作的,这是已知的,这段一共工作了j天,花费也是确定的 j*(j+1)/2,要让dp[i][j]取得最优解,那就只有让dp[i-j][0]取得最优解,即先确定前i-j天能取得的最优解,然后从第i-j+1天一直工作到第j天,这段dp[i][j]就都能取得最优解。dp[i][0]表示第i天是休息的,放假也算休息,在第i天休息的情况下,从前i天里进行抉择能取得的最大工作天数,这其实还是得从前1天,前2天,,,前i-1天中取最大值,所以dp[i][0]存储的就是前i-1天里进行选择能取得的最大工作天数,这个值要从dp[1][0],dp[1][1],dp[2][0],dp[2][1],dp[2][2],....,,dp[i-1][0],dp[i-1][1],dp[i-1][2],..dp[i-1][i-1]中取最大的,但这个定义其实是递归的,dp[i-1][0]其实表示的就是前i-2天能取得的最优解了,所以dp[i][0]只需要从dp[i-1][0],dp[i-1][1],......dp[i-1][i-1]中选最大值,最后dp[N+1][0]就是前N天进行选择能取得的最大值。

cost[i][j]对应的是dp[i][j]这种情况下一共花费了多少,比如dp[4][0]和cost[4][0]如果已知,下一步就可以去更新dp[5][1],cost[5][1],以及dp[6][2],cost[6][2],以此类推,比如从cost[4][0]更新cost[5][1]的时候,第4天是休息的,第五天是新的开始工作的第一天,花费是1,更新cost[6][2]的时候,第5天和第6天是新的开始工作的第1和第2天,花费了1和2,如果cost[4][0]加上这些花费超过了题目限制K,就不可取。就是说,cost数组是考虑题目的限制K,从某个dp[i][0]开始,往后更新是有限度的,花费上限K算一个,另外,当题目给的字符串的第i+j位置是0时,这天强制休息,这时,从第i+1天开始连续工作到第i+j天时就必须中断了。

代码用ll是最近做题做的数组什么的没改直接用了,这个题int就行了。

#include <bits/stdc++.h>
#define ll long long
#define INF 123412341
using namespace std;

ll dp[410][410];
ll cost[410][410];
char ss[410];
ll N,K;

int main()
{
    cin>>N>>K;
    scanf("%s",ss+1);
    memset(dp,0,sizeof(dp));
    memset(cost,0,sizeof(cost));

    ll jia=1;
    while(ss[0+jia]!='0'&&0+jia<=N)//从第一天开始连续工作,一直到花费用完为止
    {
        if(cost[0][0]+ jia*(jia+1)/2 <=K)
        {
                dp[0+jia][jia]=dp[0][0]+jia;
                cost[0+jia][jia]=cost[0][0]+jia*(jia+1)/2;
        }
        jia++;
    }
    
    for(ll i=1;i<=N+1;i++)
    {
        for(ll j=1;j<=N+1;j++)
        {
            if(ss[i]=='0') //为0则第I天只能休息,第I天就不能是工作中的第某一天,下面全赋值0
            {
                for(ll t=1;t<=i;t++)
                {
                    dp[i][t]=0;
                    cost[i][t]=INF;
                }
            }
            
            //求dp[i][0],也就是求前i-1天能达到的最大工作天数
            for(ll t=0;t<=i-1;t++)
            {
                if(dp[i-1][t]>dp[i][0])
                {
                    dp[i][0]=dp[i-1][t];
                    cost[i][0]=cost[i-1][t];
                }
                else if(dp[i-1][t]==dp[i][0]&&cost[i-1][t]<cost[i][0])
                {
                    cost[i][0]=cost[i-1][t];
                }
            }

            //dp[i][0]已经确定为最优解,用它更新后面更大的情况
            ll jia=1;
            while(ss[i+jia]!='0'&&i+jia<=N)
            {
                if(cost[i][0]+ jia*(jia+1)/2 <=K)
                {
                    if(dp[i+jia][jia]<dp[i][0]+jia)
                    {
                        dp[i+jia][jia]=dp[i][0]+jia;
                        cost[i+jia][jia]=cost[i][0]+jia*(jia+1)/2;
                    }
                    else if(dp[i+jia][jia]==dp[i][0]+jia)
                    {
                        cost[i+jia][jia]=min(cost[i+jia][jia],cost[i][0]+jia*(jia+1)/2);
                    }
                }
                jia++;
            }
        }
    }
    cout<<dp[N+1][0]<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/trany_lin/article/details/85847499
今日推荐