學習總結 線性dp的分析過程

動態規劃
將問題分成一個一個的小部分,每個部分對應一個或幾種狀態,通過分析每個小部分的狀態,找出一個子問題的各種狀態與下一個子問題狀態之間的關係,層層遞進,最終找出問題的最終結果。
線性dp是動態規劃問題較爲簡單的一種。
分析問題的一般步驟
1 階段點的選取,或者說是基準的選取,這一步是所有dp問題的關鍵,只有找到合適的階段,問題纔有解決的可能。
2 找出階段點所表示的含義,他具體代表什麼,是代表他之前的階段的最優值還是他之後的最優值,還是某種狀態下的最優值,有時找到的是每種狀態下的最優值,最後還要在dp【n】【i】(i屬於1到n)中選擇最優。
3 找出這個階段點有幾種狀態,給每種狀態找到一種表示,找出這種狀態與之前的狀態的關係,這時狀態轉移方程或者說轉移關係就基本上明確了。
4然後將數據想辦法處理,將關係用數組遞推的形式表示出來,這或許很不好弄,可以縮小數據的範圍,通過起始點以及向後的一些點來找找數據處理的過程和代碼實現的過程中應該注意的點,也可以多借鑑,多學習,但階段點的選取與階段點狀態的找出和狀態與之前狀態的關係的建立需要自己多學習總結思路和方法,找出自己所選取的階段點與狀態和優秀的思路和代碼之間的差別,這很重要。代碼或許僅靠自己是無法較好的完成的,但代碼的大體過程以及每一步的目的一定要先明確下來,再去寫,再去借鑑。
5 把握一個原則,原則上1秒鐘執行1億次循環循環體中的代碼處理過程稍複雜的話就一定會超時,這時數據量是一個提示,會告訴你應該用幾重循環來解決問題以及優化的大體方向是什麼。
問題分析

給定 N (1 <= N <= 1000000) 個絕對值不超過 32768 的整數(可能爲負數)組成的序列a[1],a[2],a[3],…,a[N],求從該序列中取出 M 個連續不相交子段,使這 M 個子段的和最大。如果子段全都是負數則最大子段和也爲負數。
輸入:有一個正整數 M 和 一個正整數 N,後面緊跟 N 個絕對值不大於 32768 的整數
輸出:最大 M 子段和
樣例輸入:
2 6 -1 4 -2 3 -2 3
樣例輸出:
8
分析過程
階段點 以每個數爲一個階段點,每個階段點代表他之前種種的情況的最優值,但是,還有段數的問題,那麼,顯然用一個一維數組是不行的,應該是二維,另一個是表示段數的情況,那麼這個階段點就是f【i】【j】,表示前i個數分成j段的最優值。
階段點的狀態 每個階段點應該有兩種狀態,一種是最後一個數與之前的數爲一組的情況,一種是最後一個數自己與爲一組的情況,那麼f【i】【j】=f【i-1】【j】+a【i】,(單獨情況),f【i】【j】=f【k】【j-1】+a【i】(自己爲一組的情況),k要保證j組夠分。
那麼 第一種狀態也要分兩種情況,既然最後一個不單獨成組,那麼i-1要夠分j組,不夠分就設爲極小值(有負數的情況若爲0可能造成不好的影響)夠分再加
a【i】,第二種狀態要取最大值,就是從j-1個數到i-1個數分成j-1個組的情況中挑出最大值。
到這裏,每一步都基本上明確了。

#include <iostream>
#include<cstdio>
using namespace std;
int a[1005];
int f[1005][1005];
int main()
{
    int m,n;
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    int ans=-0x3f3f3f3f;
    for(int j=1;j<=m;j++)
    {
        for(int i=j;i<=n;i++)
        {
            if(i-1<j)
            {
                f[i][j]=-0x3f3f3f3f;
            }
        else f[i][j]=f[i-1][j];
        for(int k=j-1;k<i;k++)
        {
            f[i][j]=max(f[i][j],f[k][j-1]);
        }
        f[i][j]=f[i][j]+a[i];
        }
    }
    printf("%d\n",f[n][m]);
    return 0;
}

但這題的數據量過大,1000010000m很可能超過一億,因而要想辦法優化,減少循環次數,減少數組大小。
那麼就去找代碼裏重複循環的東西,重複使用的東西,把他換掉。有了這樣的思路之後,再去借鑑方法時目的就很明確了。(1滾動數組,2記住有用的狀態值)。

小zc現在有三個字符串,他想知道前兩個字符串能不能生成第三個字符串,生成規則如下:第一個串的每個字符都可以往第二個串的任意位置插入(包括首尾位置),但必須保證來源於第一個串中的字符在生成後的串中的相對順序不可以改變。
舉個例子:
String A: cu
String B: mt
那麼,A和B可以生成的所有字符串是{cumt,cmut,cmtu,mcut,mctu,mtcu},而不能生成ucmt,因爲uc來源於A串,但改變了A中字符原來的相對順序。
但小zc覺得這個問題太簡單,於是他想讓你計算對於給定的A, B, C串,共有多少種方案能夠生成C串。方便起見,你只需要輸出最後答案對1000000007取模的值。

分析過程,階段點,以c串爲基準選取階段點,既然是匹配c串,那麼,c串上的每一個點都可以作爲一個階段點,表示c串上的這個點被匹配成功後的方案數,既然是用a,b兩個串來匹配,那麼就應該是二維數組來表示,然後,就是階段點的狀態,那麼狀態要涵蓋面廣,就是要包含匹配到c串某一個字符後,所有的方案數,那麼f【i】【j】就可以表示a串用了i個,b串用了j個之後所包含的方案數,然後再對f【i】【j】進行分析,分三種情況,一種是隻有a的情況,一種是隻有b的情況,然後是既有a又有b的情況,
f【0】【j】=f【0】【j-1】,只有a的情況,只有b的情況與之類似。
f【0】【0】=1,如果a爲零個b爲零個c爲零個,那麼方案是一。
兩者都有的情況那麼匹配成功,則s【i】=s【i+j】,表示a的,j表示b的,
f【i】【j】=f【i-1】【j】+f【i】【j】;分析到這裏,再去寫代碼基本上就能寫個大概的樣子出來了,但是,仍有很大的可能不完善,或者有缺失,這些需要多看一些優秀的代碼,多學習其中的技巧。

農夫約翰的牛想跳上月亮,就像牛在他們最喜歡的童謠。不幸的是,牛不能跳。當地的巫醫把P(1<=P<=150,000)藥水混合在一起,以幫助牛尋求跳躍。這些藥劑必須按照它們創建的順序正確地進行管理,儘管有些藥劑可能會被跳過。每種藥劑都有一個“力量”(1<=強度<500),可以增強牛的跳躍能力。在奇數的時間步驟中服用藥劑會增加牛的跳躍;在偶數的時間步驟中服用藥劑會減少跳躍。在服用任何藥劑之前,牛的跳躍能力當然是0。任何藥劑都不能服用兩次,一旦牛開始服用藥劑,必須在每一時間步驟中服用一種藥劑,從時間1開始。每輪可以跳過一個或多個藥劑。確定要服用哪種藥劑才能跳得最高。

Line 1: A single integer, P

  • Lines 2…P+1: Each line contains a single integer that is the strength of a potion. Line 2 gives the strength of the first potion; line 3 gives the strength of the second potion; and so on.
    Output
  • Line 1: A single integer that is the maximum possible jump.
    Sample Input
    8
    7
    2
    1
    8
    4
    3
    5
    6
    題目分析
    階段點 以每個時刻爲階段點,由於該題的數據量爲150000,所以顯然要用一重循環,每個階段點表示選擇該時刻服用藥物後所能達到的最大高度,但又分奇數時刻和偶數時刻,因而每個點對應兩個狀態,一個表示奇數時刻,一個表示偶數時刻,f[i][1]=max(f[i-1][1],f[i-1][0]+a[i]); f[i][0]=max(f[i-1][0],f[i-1][1]-a[i]);
    注意一點,偶數個與偶數個狀態比較,奇數個與奇數個比較,不然就亂套了。

將N分爲若干個不同整數的和,有多少種不同的劃分方式,例如:n = 6,{6} {1,5} {2,4} {1,2,3},共4種。由於數據較大,輸出Mod 10^9 + 7的結果即可。
Input
輸入1個數N(1 <= N <= 50000)。
Output
輸出劃分的數量Mod 10^9 + 7。
Sample Input
6
Sample Output
4
這題階段點的選取很重要,將1~n中每一個數字作爲一個階段點,,每個階段點對應的狀態有很多,一個階段點對應這些狀態----這個點分爲1個,兩個,。。。。多個數的和的方案數,由於狀態很多,所以,用f[i][j]表示數i分成j個數的方案數,那麼最終的方案數就等於f【n】【1.。。。m】相加。那麼接下來就是找出狀態之間的關係,那麼f【i】【j】如何由之前得到呢?這是個問題。
這時可以列表,一到n爲行,一到m爲列,寫出一部分數來,再找找規律,類似放蘋果那道題。

dp問題很難,但是一點一點的按部分分析,明確每一步的目的,即使可能不能獨立的完成某個問題,但是每一步的目的明確,分析過程明確,離最終的目標總會一點一點慢慢接近,如果上來就想寫出狀態轉移方程,這是不可能的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章