塔
洛谷题号:P1651
出处: 全国信息学分区联赛模拟试题(6)T1
主要算法:动态规划
难度:4.5
https://www.luogu.org/problemnew/show/P1651
思路分析:
思路特别有意思,f[i][j]表示使用前i块木块,高度差为j时较高的塔的最大高度。
这道题有点考察dp的基本功,f[i][j]能由那些状态转移而来?
考虑每一块木块,可以选择不用,放在左边,放在右边,三种。
如果不用,那很简单,直接由f[i-1][j]转移而来。
如果放在较高的那一堆,那么高度差反而会变大。当前差值为j,那么是由j-a[i]转移过来的,所以由f[i-1][j-a[i]]转移而来,并且要加上a[i]。前提是j>=a[i]
如果放在较低的那一堆,那么需要分类讨论:放完之后低的那一堆反而会比原先高的那堆高,还是仍然低于高的那一堆?对于第一种情况,需要满足的是j<=a[i],因为原先要比高的那堆低,所以是由f[i-1][a[i]-j]转移而来的,并且还需要加上新增的高度j。前提是a[i]>=j。对于后一种情况,是由a[i]+j转移过来的,同时最大值不变。
就这样,答案就是f[n][0],当然要特判一下如果f[n][0]==0,那么输出-1.
代码注意点:
F数组先置成-∞,因为有的高度差是根本达不到的,这是如果是0转移就会出现错误。
1 /*By QiXingzhi*/ 2 #include <cstdio> 3 #include <cstring> 4 #define N (53) 5 #define INF (0x3f3f3f3f) 6 #define Max(a,b) (((a)>(b))?(a):(b)) 7 #define Min(a,b) (((a)<(b))?(a):(b)) 8 #define read(x) x=__get_integer__() 9 typedef long long ll; 10 using namespace std; 11 inline int __get_integer__(){ 12 int x = 0; int w = 1; register char c = getchar(); 13 while(c ^ '-' && (c < '0' || c > '9')) c = getchar(); 14 if(c == '-') w = -1, c = getchar(); 15 while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar(); 16 return x * w; 17 } 18 #define __file(x) freopen(x".in","r",stdin); 19 #define __file_inout(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout); 20 int n,t,ans; 21 int a[N],f[N][500010]; 22 int main(){ 23 // __file(); 24 read(n); 25 for(int i = 1; i <= n; ++i){ 26 read(a[i]); 27 t += a[i]; 28 } 29 memset(f,-0x3f,sizeof(f)); 30 f[0][0] = 0; 31 for(int i = 1; i <= n; ++i){ 32 for(int j = 0; j <= t; ++j){ 33 f[i][j] = Max(f[i][j], f[i-1][j]); 34 f[i][j] = Max(f[i][j], f[i-1][j+a[i]]); 35 if(j >= a[i]){ 36 f[i][j] = Max(f[i][j], f[i-1][j-a[i]] + a[i]); 37 } 38 if(j <= a[i]){ 39 f[i][j] = Max(f[i][j], f[i-1][a[i]-j] + j); 40 } 41 } 42 } 43 if(f[n][0] == 0) printf("-1"); 44 else printf("%d",f[n][0]); 45 return 0; 46 }
2018-06-10 11:32:49