有N根草,编号0至N-1。任务是剪草,使N根草的高度和不超过H。
在第0时刻,第i棵小草的高度是h[i] 。
接下来的每个整数时刻,依次执行:
(1)每根草i长高grow[i]。
(2)让某根草高度归零。注意:这棵小草并没有死掉,它下一秒还会生长。
(3)计算N根草的高度和,若不超过H则任务完成。
计算最早在第几时刻,任务会被完成?
如果一开始就完成了,输出0;如果永远不可能完成,输出-1。
否则输出一个最早的完成时刻。
1 ≤ N ≤ 50 1 ≤ N ≤ 50 1≤N≤50, 0 ≤ H ≤ 1 0 6 0 ≤ H ≤ 10^6 0≤H≤106, 0 ≤ h [ i ] ≤ 1 0 5 0 ≤ h[i] ≤ 10^5 0≤h[i]≤105, 1 ≤ g r o w [ i ] ≤ 1 0 5 1 ≤ grow[i] ≤ 10^5 1≤grow[i]≤105。
如果一开始就不超过 H H H直接输出 0 0 0。
显然每根草只需要剪一次,多剪没用。
并且设完成时刻为
X
X
X,
1
1
1~
X
X
X的每一个时刻都要剪掉一根草。
长得快的草显然不可能先剪,所以一定是先剪长得慢的草。
所以我们会先剪长得慢的,再剪长得快的。
按照
g
r
o
w
[
i
]
grow[i]
grow[i]排序,一个个剪,计算最后的高度和。吗?
前面有些草可能不必剪。
如果要拿现在计算出来的结果,去掉不用剪的部分,那也不行(影响很复杂)。
考虑其它方式。
既然不能倒着来,那就顺着推。
从
1
1
1时刻开始,选择要剪掉的草。如果剪掉了生长速度第
x
x
x慢的,那么
1
1
1~
x
x
x慢的都不会再选。
所以,记
F
[
i
]
[
x
]
F[i][x]
F[i][x]表示到第
i
i
i时刻,最多选择到了第
x
x
x根草的最小高度和。
那么按顺序枚举
i
i
i,每个
i
i
i枚举
i
−
1
i-1
i−1的
x
1
x_1
x1和
i
i
i的
x
2
x_2
x2然后做转移。
F
[
i
]
[
x
2
]
=
m
i
n
{
F
[
i
−
1
]
[
x
1
]
+
g
r
o
w
_
s
u
m
−
h
[
x
2
]
−
g
r
o
w
[
x
2
]
∗
i
}
F[i][x_2]=min\{F[i-1][x_1]+grow\_sum-h[x_2]-grow[x_2]*i\}
F[i][x2]=min{F[i−1][x1]+grow_sum−h[x2]−grow[x2]∗i}
ps. N怎么这么小(
本机对拍ac
网上好多题解好像都没有输出0的概念,一开始任务就完成的也照样输出1...
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
int N,H,pre=0;
pair<int,int>grass[55]={};
int sum=0,f[55][55]={};
int main(){
scanf("%d%d",&N,&H);
for(int i=1;i<=N;++i)scanf("%d",&grass[i].second),pre+=grass[i].second;
for(int i=1;i<=N;++i)scanf("%d",&grass[i].first),sum+=grass[i].first;
sort(grass+1,grass+1+N);
if(pre<=H){printf("0");return 0;}
for(int i=1;i<=N;++i)for(int j=1;j<=N;++j)f[i][j]=0x3f3f3f3f;
for(int i=0;i<=N;++i)f[0][i]=pre;
for(int i=1;i<=N;++i){
for(int j=1;j<=N;++j){
for(int k=i-1;k<j;++k){
f[i][j]=min(f[i][j],f[i-1][k]+sum-grass[j].second-grass[j].first*i);
if(f[i][j]<=H)
{
printf("%d",i);
return 0;
}
}
}
}
printf("-1");
return 0;
}
数据生成器
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<cctype>
#include<cmath>
#include<cstring>
#include<queue>
#include<sstream>
using namespace std;
#define ll long long
ll GenRand(const ll Lim1,ll Lim2)
{
++Lim2;
ll ret=Lim1;
int t=0;
while(t<100)
{
if(rand()/(RAND_MAX+1.0)<0.1)break;
ret+=rand();
ret%=Lim2;
++t;
}
while(ret<Lim1)ret+=Lim1;
ret%=Lim2;
return ret;
}
int N,T;
stringstream ss;
int main(int argc,char**argv)
{
int seed=time(NULL);
if(argc>1)
{
ss.clear();
ss<<argv[1];
ss>>seed;
}
srand(seed);
N=GenRand(1,50),T=GenRand(0,1000000);
printf("%d %d\n",N,T);
for(int i=1;i<=N;++i)printf("%d ",GenRand(0,100000));printf("\n");
for(int i=1;i<=N;++i)printf("%d ",GenRand(0,100000));
return 0;
}
对拍bat↓
@echo off
:loop
data_generator.exe %random% > data.in
std.exe < data.in > std.out
my.exe < data.in > my.out
fc my.out std.out
if not errorlevel 1 goto loop
pause
goto loop