快手歷屆筆試題(2)
技術標籤:常見筆試程式設計題
題目描述
又到了吃午飯的時間,你和你的同伴剛剛研發出了最新的GSS-483型自動打飯機器人,現在你們正在對機器人進行功能測試。
為了簡化問題,我們假設午飯一共有N個菜,對於第i個菜,你和你的同伴對其定義了一個好吃程度(或難吃程度,如果是負數的話……)A[i],
由於一些技(經)術(費)限制,機器人一次只能接受一個指令:兩個數L, R——表示機器人將會去打第L~R一共R-L+1個菜。
本著不浪費的原則,你們決定機器人打上來的菜,含著淚也要都吃完,於是你們希望機器人打的菜的好吃程度之和最大
然而,你善變的同伴希望對機器人進行多次測試(實際上可能是為了多吃到好吃的菜),他想知道機器人打M次菜能達到的最大的好吃程度之和
當然,打過一次的菜是不能再打的,而且你也可以對機器人輸入-1, -1,表示一個菜也不打
輸入描述:
第一行:N, M
第二行:A[1], A[2], ..., A[N]
輸出描述:
一個數字S,表示M次打菜的最大好吃程度之和
示例1
輸入
7 2
1 2 3 -2 3 -10 3
輸出
10
說明
[1 2 3 -2 3] -10 [3]
示例2
輸入
7 4
1 2 3 -2 3 -10 3
輸出
12
說明
[1 2 3] -2 [3] -10 [3]
第四次給機器人-1, -1的指令
備註:
N <= 10^5 = 100000 |A[i]| <= 10^4 = 10000 10%資料M = 1 50%資料M <= 2 80%資料M <= 100 100%資料M <= 10^4 = 10000
思路:常規思路:dp[i][j]表示前i個數切分不超過j段的最大和,則dp[i][j]=min(dp[i-1][j],dp[k][j-1])+a[i],其中j-1<=k<i,這是很好理解的,dp[i-1][j]表示最後一段以j結尾的最大和,dp[k][j-1]表示第j個數開始新開闢了一段的最大和。
根據上述轉移方程我們不難在O(m*n*n)的時間複雜度內解決,然而,題目的資料量使得我們無法通過這種方法解決這道題,另闢奇徑:滾動陣列!
我們發現在轉移方程中第二維永遠只和當前段數減一有關,因此我們其實是沒必要儲存這一維的,此外,我們仔細品一下這個轉移方程,你們覺得dp[k][j-1]需要求嗎?
其實我們在求dp[i][j]的時候已經求出來所有的dp[k][j-1]的值了,呢直接通過一箇中間變數把這個值利用起來就好啦,這樣我們能夠在降低一維空間複雜度的基礎上再降低一維時間複雜度!
然而上述優化完成後,最後還是隻能過80%的資料,我們只能考慮騙資料的優化:將連續的正數或負數合併成一個數,從而縮小原陣列的長度,最終AC!
#include<stdio.h>
#include<algorithm>
using namespace std;
#define maxn 100005
int n,m,a[maxn],dp[maxn],cnt,ans;
int main(void){
scanf("%d%d",&n,&m);
for(int i=0,x;i<n;i++){
scanf("%d",&x);
if(cnt==0)
a[++cnt]=x;
else{
if(a[cnt]*x>0)
a[cnt]+=x;
else
a[++cnt]=x;
}
}
n=cnt;
for(int i=1;i<=m;i++){
int sum=0;
for(int j=1;j<i;j++)
sum+=a[j];
ans=sum;
for(int j=i;j<=n;j++){
sum=max(sum, dp[j-1])+a[j];
dp[j-1]=ans;
ans=max(ans, sum);
}
ans=max(ans, sum);
}
printf("%d\n",ans);
return 0;
}