1. 程式人生 > >[NOIP2012模擬10.25] 剪草 [貪心+dp]

[NOIP2012模擬10.25] 剪草 [貪心+dp]

有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 0 H
1 0 6 0 ≤ H ≤ 10^6
0 h
[ i ] 1 0 5 0 ≤ h[i] ≤ 10^5
1 g r o w [ i ] 1 0 5 1 ≤ grow[i] ≤ 10^5

如果一開始就不超過 H H 直接輸出 0 0

顯然每根草只需要剪一次,多剪沒用。
並且設完成時刻為 X X 1 1 ~ X X 的每一個時刻都要剪掉一根草。

長得快的草顯然不可能先剪,所以一定是先剪長得慢的草。
所以我們會先剪長得慢的,再剪長得快的。
按照 g r o w [ i ] grow[i] 排序,一個個剪,計算最後的高度和。嗎?

前面有些草可能不必剪。
如果要拿現在計算出來的結果,去掉不用剪的部分,那也不行(影響很複雜)。

考慮其它方式。
既然不能倒著來,那就順著推。
1 1 時刻開始,選擇要剪掉的草。如果剪掉了生長速度第 x x 慢的,那麼 1 1 ~ x x 慢的都不會再選。
所以,記 F [ i ] [ x ] F[i][x] 表示到第 i i 時刻,最多選擇到了第 x x 根草的最小高度和。
那麼按順序列舉 i i ,每個 i i 列舉 i 1 i-1 x 1 x_1 i i x 2 x_2 然後做轉移。
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\}

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