来了来了。
题目:
给你n个数,你一次操作可以把某一个数-1(可以减为负数),你的目标是使任意的k个数严格小于它旁边的两个数(第一个数只用严格小于第二个数,第n个数只用严格小于第n-1个数),问最少需要几次操作。k是不确定的,请输出1<=k<=n/2(向上取整)时的答案。
输入格式:
第一行一个正整数n
第二行n个正整数ai
输出格式:
一行 1<=k<=n/2 个数,第i个数代表k=i时的答案
数据范围:
1 ≤ n ≤ 5000
1 ≤ ai ≤ 100000
5
1 1 1 1 1
1 2 2
3
1 2 3
0 2
思路:
看到这道题的第一眼,我想到了一道题,2018noipD1T1铺设道路(链接:https://www.luogu.com.cn/problem/P5019),看起来十分相似,于是这道题想用朴素的贪心写,结果果然不对,两道题的区别还是很大,贪心的正确性无法保证,所以还是回来老老实实的写动归。
我们设定一个三维数组f,f[i][j][0]表示前i座山,修建j座房子,且第i座山没有建房子的代价,f[i][j][1]表示前i座山,修建j座房子,且第i座山建有房子的代价。
通过简单的模拟我们可以得到,(1)f[i][j][0]会从f[i-1][j][0](直接从前一个状态搬过来,前一座山和这座山都不建房子,抹油代价)和f[i-1][j][1](前一座山已经建了房子,那么就要使这座山的高度低于前一座山的高度,代价为max(0,a[i]-a[i-1]+1),有0是因为可能这座山的高度原来就低于前一座山的高度)转移过来,转移方程为:f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]+max(0,a[i]-a[i-1]+1)) (2)f[i][j][1]的处理要比f[i][j][0]的处理相对复杂,在此我们明确一个观点,相邻的两座山不可能都建房子,并且在考虑这座山是否建房子的时候,可以只考虑这座山之前的状态,而不去考虑这座山之后的状态,这座山之后的状态我们可以在之后再处理,因此,f[i][j][1]可以从f[i-2][j-1][0]转移过来,因为第i-2座山没有建房子,所以第i-1座山的高度没有变化,代价为max(0,a[i]-a[i-1]+1)。f[i][j][1]同样可以从f[i-2][j-1][1]转移过来,因为第i-2座山已经建有房子,所以第i-1座山的高度可能会有变化,因此,总代价为:max(0,a[i-1]-(a[i-2],a[i])+1)(大家思考一下,为什么这里总代价不是:max(0,a[i-1]-min(0,a[i-1]-a[i-2]+1)-a[i]+1)) 答案(先思考再看答案):因为第i-1座山的高度一定比第i-2和i座山低,而这样做只能保证第i-1座山的高度低于第i-2座山的高度,无法保证第i-1座山的高度低于第i座山的高度,因此,我们也可以改成:max(max(0,a[i-1]-a[i]+1),max(0,a[i-1]-a[i-2]+1)) 贼长)
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const int maxn=5e3+5,maxe=5e3+5,INF=0x3f3f3f3f; 8 int n,m,a[maxn],f[maxn][maxn][3],ans; 9 inline int read(){ 10 int s=0,t=1; 11 char ch=getchar(); 12 while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();} 13 while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar(); 14 return s*t; 15 }//朴素快读 16 int main(){ 17 freopen("a.in","r",stdin); 18 n=read(); 19 for(int i=1;i<=n;i++)a[i]=read(); 20 a[0]=INF; 21 memset(f,0x3f,sizeof(f)); 22 f[0][0][0]=f[1][0][0]=f[1][1][1]=0;//初始化 23 for(int i=2;i<=n;i++){ 24 f[i][0][0]=f[i-1][0][0]; 25 for(int j=1;j<=(i+1)/2;j++){//这里我测了一下,不管是用floor+1还是用ceil都不行,神奇 26 f[i][j][1]=min(f[i-2][j-1][0]+max(0,a[i-1]-a[i]+1),f[i-2][j-1][1]+max(max(0,a[i-1]-a[i]+1),max(0,a[i-1]-a[i-2]+1))); 27 f[i][j][0]=min(f[i-1][j][0],f[i-1][j][1]+max(0,a[i]-a[i-1]+1)); 28 } 29 //cout<<floor(i/2)+1<<" "<<(i+1)/2<<endl; 30 } 31 for(int i=1;i<=(n+1)/2;i++)cout<<min(f[n][i][0],f[n][i][1])<<" "; 32 return 0; 33 }
嘤嘤嘤,溜了