1. 程式人生 > >【noip模擬】德充符

【noip模擬】德充符

amp 時間限制 一行 我們 但是 i++ 如果 time shuffle

時間限制:2s
內存限制:512MB

【題目描述】
申徒嘉和鄭子產都是伯昏無人的學生,子產因為申徒嘉是殘疾人,非常看不起他,於是
想要刁難他。
子產給了申徒嘉 n個數 a1,a2...an。
現在他要求申徒嘉重新排列這些數,使得 H=||...|b1-b2|-b3|-b4|-...|-bn|最大(b 是
a 重新排列後的序列,|x|表示取 x的絕對值)
申徒嘉對於吹逼很擅長,但是數學就不怎麽樣了,於是他請你來幫幫他。

【輸入格式】
第一行一個數 n,接下來一行n個數,第 i 個數表示a[i]
n<=300
1<=a[i]<=300
對於30%的數據,n<=10

【輸出格式】
輸出一行一個整數表示答案



【樣例輸入】
4
3 6 7 8

【樣例輸出】
6

【樣例解釋】
對於第一組樣例:
|||6-8|-3|-7| = 6

【題目分析】

亂搞做法:

因為答案不超過300,而且似乎要控制只有少數特定的排列使得答案最大是不太容易的,因此不斷隨機打亂更新答案,就很容易得到最大值。

標準做法:

考慮堆積木模型:

現在你有兩個塔,初始高度都是0,n個積木,每個積木的高度是a[i],現在你每次可以選一個積木放到當前高度較矮的那個塔上,最大化最後兩個塔的高度差

容易證明這個問題和原問題等價

現在考慮怎麽解決這個問題,我們發現每次只能往較矮的那個塔上放積木這個限制非常麻煩。

不難證明最高的一個積木一定是最後放的(試想把最高的積木移到中間上,結果一定不會變優)

現在我們把最高的一個積木拿掉,這樣問題就變成了最小化高度差

如果要最小化高度差的話,每次只能往較矮的那個塔上放積木這個條件是沒有用的(要得到最後的最優解,一定存在一種方案是滿足這個限制的)

因此就去掉了這個限制,接下來愉快地DP就好了。

設F[i][k]表示只用前i個,兩邊的高度差達到k可行不可行,轉移:

轉移:

if(f[i-1][k])

F[i][k+a[i]] = F[i][abs(k-a[i])] = true

【code】

#include<iostream>
#include
<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #include<ctime> #include<vector> using namespace std; const int N = 305; int n, a[N]; inline int read(){ int i = 0, f = 1; char ch = getchar(); for(; (ch < 0 || ch > 9) && ch != -; ch = getchar()); if(ch == -) f = -1, ch = getchar(); for(; ch >= 0 && ch <= 9; ch = getchar()) i = (i << 3) + (i << 1) + (ch - 0); return i * f; } inline void wr(int x){ if(x < 0) putchar(-), x = -x; if(x > 9) wr(x / 10); putchar(x % 10 + 0); } int main(){ freopen("dcf.in", "r", stdin); freopen("dcf.out", "w", stdout); n = read(); srand(time(0)); for(int i = 1; i <= n; i++) a[i] = read(); int T = 200000, ans = -1; while(T--){ random_shuffle(a + 1, a + n + 1); int sum = a[1]; for(int i = 2; i <= n; i++) sum = abs(sum - a[i]); if(sum > ans) ans = sum; } wr(ans); return 0; }

【noip模擬】德充符