試題 演算法提高 天天向上-dp
試題 演算法提高 天天向上
問題描述 A同學的學習成績十分不穩定,於是老師對他說:“只要你連續4天成績有進步,那我就獎勵給你一朵小紅花。”可是這對於A同學太困難了。於是,老師對他放寬了要求:“只要你有4天成績是遞增的,我就獎勵你一朵小紅花。”即只要對於第i、j、k、l四天,滿足i<j<k<l並且對於成績wi<wj<wk<wl,那麼就可以得到一朵小紅花的獎勵。現讓你求出,A同學可以得到多少朵小紅花。 輸入格式 第一行一個整數n,表示總共有n天。第二行n個數,表示每天的成績wi。 輸出格式 一個數,表示總共可以得到多少朵小紅花。 樣例輸入 61 3 2 3 4 5 樣例輸出 6 資料規模和約定 對於40%的資料,n<=50;
對於100%的資料,n<=2000,0<=wi<=109
分析:
我們將 dp[i][j] 定義為以a[i]為起點,一直到陣列結束為止,所有遞增序列長度為j的序列的個數。
以陣列 1 3 2 3 4 5為例:
dp[3][2]表示從第二個3為起始,一直到5,遞增序列長度為2的個數。容易知道,滿足這樣的序列有2個,34 和 35。所以dp[3][2]=2;
有了上述的定義,我們就可以得出以下遞推公式
dp[i][j]= ∑dp[k][j-1] (k>i,a[k]>a[i])
現在,我們只要確定了邊界條件,就可以使用動態規劃來解決這個問題了。
容易知道 dp[n-1][1]是邊界條件,值為1。
為了讓各位讀者對動態規劃的過程有更形象的瞭解,我就以1 3 2 3 4 5 為例,列出開頭的幾個步驟:
初始: dp[5][1]=1,其餘dp[i][j]=0;
第二步 : dp[4][1]=1; dp[4][2]=dp[5][1]=1;
第三步: dp[3][1]=1; dp[3][2]=dp[4][1]+dp[5][1]=2; dp[3][3]=dp[4][2]=1;
第四步:dp[2][1]=1; dp[2][2]=dp[5][1]+dp[4][1]+dp[3][1]=3; dp[2][3]=dp[3][2]+dp[4][2]=3; dp[2][4]=dp[3][3]=1;
第五步: dp[1][1]=0; dp[1][2]=dp[4][1]+dp[5][1]=2 (a[k]要大於a[i])
dp[1][3]=dp[4][2]=1;
第六步: dp[0][1]=1; dp[0][2]=dp[1][1]+dp[2][1]+dp[3][1]+dp[4][1]+dp[5][1]=5; … dp[0][4]=dp[1][3]+dp[2][3]+dp[3][3] =1+3+1=5;
因此,只要計算所有的dp[i][4]的和即可。 注意:要long long,否則只會通過40%的樣例 程式碼:
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; typedef long long ll; const int maxn = 2001; ll a[maxn],dp[maxn][5]; int main(){ int n; cin>>n; for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); } memset(dp,0,sizeof(dp)); for(int i=n;i>=1;i--){ for(int j=2;j<=4;j++){ dp[i][1] = 1; int k=i+1; while(k<=n){ if(a[k]>a[i]) dp[i][j]+=dp[k][j-1]; k++; } } } ll sum=0; for(int i=1;i<=n;i++){ sum+=dp[i][4]; } cout<<sum<<endl; return 0; }