Making the Grade(POJ3666)


題目大意:

給出長度為n的整數數列,每次可以將一個數加1或者減1,最少要多少次可以將其變成單調增或者單調減(不嚴格).

 

題解:

1.一開始我有一個猜想,就是不管怎么改變,最終的所有數都是原來的某個數。然而我並不會證明,然而我屬於那種不徹底弄清楚就不會去寫的那種頑固分子,於是就拖了好幾天。網絡上有很多關於此題的題解,確實用了這個猜想來離散化,但是都是講怎么dp,然后最后扯一句“由於數據比較大,可以離散化”之類的話,要么就是相當粗略的證明(也許已經說的夠清楚了只不過我沒理解...)。

2.今天早上起來洗漱的時候,感覺頭腦比較清醒,再次想了一下這個問題,想到一個自認為正確的證明:

記原來的數從小到大排序后分別是$a_1\ a_2\ a_3\cdots a_n$ 修改后從左到右分別是$b_1\ b_2\ b_3\cdots b_n$. 為了方便描述,在數軸上標出這些點,稱為關鍵點。 

假設存在$a_s<b_i<=b_{i+1}<=\cdots <=b_j<a_{s+1}$    

情況一:如果這些b都相等,那么把這些b都改成$a_s$或者$a_{s+1}$ 肯定會有一種更優。

情況二:如果不全相等,那么肯定存在 $b_p\ b_{p+1}\ b_{p+2}\cdots b_q$,他們的值相等,那么把他們移到左邊的關鍵點或者右邊的關鍵點,肯定有一種會更加優. 不斷這樣移動,最后就變成情況一了。

綜上至少存在一種最優方案,最終的所有數都是原來的某個數。

因此可以離散化之后做dp,dp[i][j]表示把前i個數變成單調增(不嚴格)且第i個數變成原來第j大的數的最小代價。

dp[i][j]=min{dp[i-1][1...j]}+abs(a[i]-b[j]).

單調減(不嚴格)的情況也一樣,更加方便的是可以把原數組倒轉后做單調增的dp。

 

另外這題和CF Codeforces Round #371 (Div. 1)的C題類似,有個超簡單的nlogn做法。可以看這篇:http://www.cnblogs.com/vb4896/p/5894578.html

 

代碼:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cmath>
 4 #include <cstring>
 5 #include <algorithm>
 6 #include <vector>
 7 #include <map>
 8 #include <cstdlib>
 9 #include <set>
10 using namespace std;
11 
12 #define X first
13 #define Y second
14 #define Mod 1000000007
15 #define N 3010
16 typedef long long ll;
17 typedef pair<int,int> pii;
18 
19 inline int Mul(int x,int y){return 1ll*x*y%Mod;}
20 inline int Add(int x,int y){return ((x+y)%Mod+Mod)%Mod;}
21 
22 int n,m;
23 int a[N],b[N];
24 ll dp[N][N],Ans=1ll<<60;
25 
26 void Solve()
27 {
28     for (int j=1;j<=m;j++) dp[1][j]=abs(b[j]-a[1]);
29     for (int i=2;i<=n;i++)
30     {
31         ll tmp=1ll<<60;
32         for (int j=1;j<=m;j++)
33         {
34             tmp=min(tmp,dp[i-1][j]);
35             dp[i][j]=abs(b[j]-a[i]);
36             dp[i][j]+=tmp;
37 
38         }
39     }
40     for (int j=1;j<=m;j++) Ans=min(Ans,dp[n][j]);
41 }
42 
43 int main()
44 {
45     //freopen("in.in","r",stdin);
46     //freopen("out.out","w",stdout);
47 
48     scanf("%d",&n);
49     for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i]=a[i]-i;
50     sort(b+1,b+n+1); 
51     for (int i=1;i<=n;i++) if (i==1 || b[i]!=b[i-1]) b[++m]=b[i];
52     
53     Solve();
54     printf("%I64d\n",Ans);
55     
56     return 0;
57 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM