剪草

剪草

又一道水题
【问题描述】
有 N 棵小草,编号 0 至 N-1。奶牛 Bessie 不喜欢小草,所以 Bessie 要用剪刀剪草,目标是使
得这 N 棵小草的高度总和不超过 H。在第 0 时刻,第 i 棵小草的高度是 h[i],接下来的每个整数时
刻,会依次发生如下三个步骤:
(1)每棵小草都长高了,第 i 棵小草长高的高度是 grow[i]。
(2)Bessie 选择其中一棵小草并把它剪平,这棵小草高度变为 0。注意:这棵小草并没有死掉,
它下一秒还会生长的。
(3)Bessie 计算一下这 N 棵小草的高度总和,如果不超过 H,则完成任务,一切结束,否则轮
到下一时刻。
你的任务是计算:最早是第几时刻,奶牛 Bessie 能完成它的任务?如果第 0 时刻就可以完成就
输出 0,如果永远不可能完成,输出-1,否则输出一个最早的完成时刻。
【输入】
第一行,一个整数 g,表示有 g 组测试数据,组数不超过四组。每组数据的格式是:
第一行,两个整数 N 和 H。 1 ≤ N ≤ 50,0 ≤ H ≤ 1000000。
第二行,N 个整数,表示 h[i]。0 ≤ h[i] ≤ 100000。
第三行,N 个整数,表示 grow[i]。1 ≤ grow[i] ≤ 100000。

一开始用了个小贪心
大循环是从小到大每局到第几天可以结束,因为可以证明这个次数不超过20,诶虽然不知道怎么怎么证
然后从枚举的这个第i天倒推回去,最后一天减去目前长的最高的,倒数第二天减去当天除去最后一天减掉过的最高的
因为我们剪掉的高度数永远不会变,所以可以这么做

#include<bits/stdc++.h>
using namespace std;

int T;
long long a[60],n,m,gr[60],suma,sumgr,b[60],c[60];
void init()
{
   suma=sumgr=0;
   for (int i=1; i<=n; i++)
      {
        scanf("%lld",&a[i]);
        suma+=a[i];
      }
   for (int i=1; i<=n; i++)
      {
        scanf("%lld",&gr[i]);
        sumgr+=gr[i];
      }
}
int main()
{
  freopen("grass.in","r",stdin);
  freopen("grass.out","w",stdout);
  scanf("%d",&T);
  for (int oo=1; oo<=T; oo++)
  {
    bool ppp=true;
    scanf("%lld%lld",&n,&m);
    init();
    if (suma+sumgr<=m) {printf("0\n"); ppp=false; continue;}
    for (int i=1; i<=n; i++)
      {
        long long sumb=0;
        for (int j=1; j<=n; j++)
          {
            b[j]=a[j]+gr[j]*i;
            c[j]=b[j];
            sumb+=b[j];
          }
        long long p=0;
        for (int j=0; j<i; j++)
          {
            long long maxx=0,maxi=0;
            for (int k=1; k<=n; k++)
              {
                b[k]=c[k]-j*gr[k];
                if (b[k]>maxx) {maxx=b[k]; maxi=k;}
              }
            sumb-=b[maxi];
            c[maxi]=0;
            if (sumb<=m) {p=1; break;}
          }
        if (p==1) {printf("%d\n",i); ppp=false; break;}
      }
    if (ppp==true) printf("-1\n");
  }

}

但是哎,毕竟是贪心,
1 50
10000 10000
1 49

这组数据,贪心旧先减去后面的,但这样会发现不对,贪心的答案是 3
但是其实先把前面的减掉,再减掉后面的就好了,所以答案是2

所以你应该想到了

DP

毕竟没法贪心的都用DP,这是铁律。

d p [ i ] [ j ] 表示第 i j 是否减的最优值。

d p [ i ] [ j ] = m a x ( d p [ i 1 ] [ j ] , d p [ i 1 ] [ j 1 ] + g r a s s [ i ] . h + g r a s s [ i ] . g r o w j ) ;


或者
不取

#include<bits/stdc++.h>
using namespace std;
int T,n,H,ans,dp[55][55],hsum,gsum;
struct node{
    int h,grow;
    bool operator < (const node b) const{
        return grow<b.grow;
    }
};
node grass[55];

int main(){
    freopen("grass.in","r",stdin);
    freopen("grass.out","w",stdout);
    scanf("%d",&T);
    while(T--){
        ans=-1;
        scanf("%d%d",&n,&H);
        hsum=gsum=0;
        for(int i=1;i<=n;i++) scanf("%d",&grass[i].h),hsum+=grass[i].h;
        for(int i=1;i<=n;i++) scanf("%d",&grass[i].grow),gsum+=grass[i].grow;
        sort(grass+1,grass+n+1);
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=i;j++)
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+grass[i].h+grass[i].grow*j);
        for(int t=0;t<=n;t++){
            int sum=hsum+gsum*t;
            if(sum-dp[n][t]<=H){
                ans=t;
                break;
            }
        }
        printf("%d\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/beautiful_CXW/article/details/82561177