1. 程式人生 > >DP——P2300 合並神犇

DP——P2300 合並神犇

i++ 最大 int getchar() 總數 for 列合並 最大的 完成後

題目背景

loidc來到了NOI的賽場上,他在那裏看到了好多神犇。

題目描述

神犇們現在正排成一排在刷題。每個神犇都有一個能力值p[i]。loidc認為坐在附近的金牌爺能力參差不齊非常難受。於是loidc便想方設法對神犇們進行人道主義合並。

loidc想把神犇的能力值排列成從左到右單調不減。他每次可以選擇一個神犇,把他合並到兩側相鄰的神犇上。合並後的新神犇能力值是以前兩位犇的能力值之和。每次合並完成後,被合並的兩個神犇就會消失。合並後的新神犇不能再分開(萬一他倆有女朋友咋辦)因此每次合並後神犇的總數會減1.

loidc想知道,想治好他的強迫癥需要合並多少次

輸入輸出格式

輸入格式:

第一行一個整數 n。

第二行 n 個整數,第 i 個整數表示 p[i]。

輸出格式:

loidc需要合並的次數

輸入輸出樣例

輸入樣例#1:
8
1 9 9 4 1 2 2 9
輸出樣例#1:
3

說明

對於 50%的數據,0< n <=5000。

對於 100%的數據,0< n <=200000,0< p[i] <=2147483647,p 均為隨機生成。

Solution:

  好久沒寫dp了啊,於是寫一發dp。

  本題要使一段序列合並成不下降序列,因為只能合並相鄰的兩個數,所以合並後的一個數必定是由原版序列中的一段進行數次合並得到的。考慮簡單的貪心思路,對於第一個數,每次不停加入一個數直到它們的和大於第一個數停止,繼續此操作,直到結束,但是這樣顯然是錯誤的,因為前面滿足了條件不一定後面會最優(很簡單思考懶的舉例了)。由貪心思路引申到dp,因為是一段合並,考慮到前綴和sum[i],我們令f[i]表示到了第i個數為止所合並的次數,用一個輔助數組maxp[i]表示到了i為止合並後最大的一個數,於是得到狀態轉移方程:if(sum[i]-sum[j]>=maxp[j])f[i]=f[j]+j-i-1(其中i>j)

代碼:

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 inline int gi()
 5 {
 6     int a=0;char x=getchar();bool f=0;
 7     while((x<0||x>9)&&x!=-)x=getchar();
 8     if(x==-)x=getchar(),f=1;
 9     while(x>=0&&x<=
9)a=a*10+x-48,x=getchar(); 10 return f?-a:a; 11 } 12 ll n,sum[200005],f[200005],maxp[200005]; 13 int main() 14 { 15 n=gi();int x,i,j; 16 for(i=1;i<=n;i++)x=gi(),sum[i]=sum[i-1]+x; 17 for(i=1;i<=n;i++){ 18 for(j=i-1;j>=0;j--) 19 if(sum[i]-sum[j]>=maxp[j])break; 20 maxp[i]=sum[i]-sum[j]; 21 f[i]=f[j]+i-j-1;maxp[i]=sum[i]-sum[j]; 22 } 23 cout<<f[n]; 24 return 0; 25 }

DP——P2300 合並神犇