動態規劃

線性動態規劃

洛谷P2285 打鼴鼠html

把鼴鼠按照出現的時間排成一列,由於必須先打早些出現的鼴鼠再打晚些出現的鼴鼠,因此在打當前鼴鼠時要先肯定上一個打的鼴鼠是誰,打到他時一共最多打了多少鼴鼠,這種思路就和LIS很像了,一點不一樣就在於並非全部t[i]<t[j]均可以先打i再打j,必須知足兩鼴鼠的曼哈頓距離小於等於他們的出現時間差ios

洛谷P1868 飢餓的奶牛
a[i][0]表示i位置是否爲某一區間的起點
a[i][1]表示以i爲起點的區間的價值
f[i]表示到i位置的最大價值是多少
由於不容許區間重疊,因此一旦選了某個區間,其餘的價值只能來自這個區間以外,考慮從前日後進行狀態轉移,那麼f[i+a[i][1]]=max{f[i]+a[i][1]} 其中i必須保證是區間i的起點數組

P3004 [USACO10DEC]寶箱Treasure Chestide

區間dp
f[i][j]表示在i,j這段區間內先手能得到的最大分數;那麼後手在先手最優方案走法下,按最優方案走的最大分數就是i,j這個區間總分數減去f[i][j].post

洛谷P3146 [USACO16OPEN]248url

區間dp,f[i][j]表示區間 i-j 合併的最大值
能夠由f[i][k],f[k+1][j]轉移過來,轉移條件爲f[i][k]==f[k+1][j] spa

 

揹包型

/*
    for(int i=1;i<=n;i++)
        for(int j=m;j>=a[i];j--)
            f[j]+=f[j-a[i]];
    這樣嗎
    爲何這樣m元錢就是恰好花完的
    
    從m開始枚舉的
    枚舉了全部的狀態
    因此從1到m都會有
    
    若是m花不完或者湊不出m
    那麼m的前繼狀態的方案數必定是0
    不管怎樣f[m]+=f[m-w[i]]
    f[m]都是0
    可是若是能夠拼出
    剛纔的枚舉就會把方案數轉移出
    由於能夠從n層枚舉摘取這樣的狀況
    f[0]=1,w[1]=1,w[2]=2,m=3
    
    f[1]+=f[1-w[1]]
    f[3]+=f[1-w[2]]
    那麼f[m]就能從f[1]轉移過來
    f[1]就能從f[0]轉移過來
*/
#include<iostream>
#include<cstdio>
using namespace std;
int f[10000],n,m,a[110];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    f[0]=1;
    for(int i=1;i<=n;i++)
        for(int j=m;j>=a[i];j--)
            f[j]+=f[j-a[i]];
    printf("%d",f[m]);
}
01揹包求方案數
/*
    輸入x,y,z表示第i個物品的重量爲x,價值爲y,屬於z組
*/
#include<iostream>
#include<cstdio>
#define maxn 1010
using namespace std;
int n,m,f[maxn],a[maxn][maxn],v[maxn],w[maxn],t;
int main(){
    scanf("%d%d",&m,&n);
    int x,y,z;
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&w[i],&v[i],&z);
        a[z][++a[z][0]]=i;t=max(t,z);
    }
    for(int k=1;k<=t;k++)
        for(int j=m;j>=0;j--)
            for(int i=1;i<=a[k][0];i++){
                if(j-w[a[k][i]]>=0)
                f[j]=max(f[j],f[j-w[a[k][i]]]+v[a[k][i]]);
            }
    printf("%d",f[m]);
}
分組揹包

 

洛谷P2854 [USACO06DEC]牛的過山車Cow Roller Coaster
看到本題不難想到二維費用的揹包f[i][j]=max{f[i-a[i].len][j-a[i].w]+a[i].v},可是題目要求全部鐵軌首尾相連,因此須要對a[]按起點排個序,剩下的就和上面的方程很像了code

 

棋盤型

洛谷P2380 狗哥採礦htm

咱們定義f[i][j]f[i][j]爲在以(i,j)(i,j)爲右下角的子矩陣中的最大采礦量,由題意咱們可知,若是(i,j)(i,j)是向左轉移礦,那麼(i,j-1)(i,j?1),必定也是向左,(i,j-2)(i,j?2)一直到(i,1)(i,1)都是向左,同理若是(i,j)(i,j)是向上轉移礦,那麼(i-1,j)(i?1,j),必定也是向上,(i-2,j)(i?2,j)一直到(1,j)(1,j)都是向左。這就能夠其實咱們用前綴和去維護一段區間的採礦量。
在轉移時,咱們只關心當前(i,j)(i,j)的採礦方向。設A[i][j]A[i][j]爲向上的前綴和,B[i][j]B[i][j]爲向左的前綴和,那麼轉移方程f[i][j]=max(f[i-1][j]+B[i][j],f[[i][j-1]+A[i][j])f[i][j]=max(f[i?1][j]+B[i][j],f[[i][j?1]+A[i][j]).blog

像這種要連續選擇的問題能夠考慮用前綴和維護

機率dp

poj3071 Football

乘法原理,設f[j][i]表示第j支球隊經過第i場比賽的機率,則:f[j][i]=sum(f[j][i-1]*f[j+k][i-1]*p[j][j+k]),其中j+k是它這一場可能面對的對手,實際上就是它上一場比賽的第一支隊伍加2^(i-1)一直加到2^1 

數位dp

反擊數

 

狀壓dp

洛谷P3070 [USACO13JAN]島遊記Island Travels

 

綜合來講這真的是一個好題,可是dp部分仍是比較基礎的

由於題目要求以連通塊爲單位,因此就先求出全部'X'所在的連通塊,flag[i][j]就是i,j位置上的點所在的聯通塊,num[i]是編號爲i的連通塊的大小,而後以每一個連通塊爲起點進行spfa,通過全部spfa後能夠得出任意兩連通塊之間的最短路。就能夠開始dp啦!
dp[S][j]表示走過點集S到達j的最短路徑,轉移:dp[S/j][k] + dis[k,j]

洛谷P2831 憤怒的小鳥

數據範圍很小,考慮狀態壓縮,把打死的豬的集合壓縮成二進制

g[i][j]:打死i,j兩頭豬的拋物線能打到的豬的集合 
f[s]:被打死的豬的集合爲s時最少打多少次 
for(int i=1;i<(1<<n);i++){
    int j=0;
    while((i&(1<<j))==0)j++;
    f[i]=f[i^(1<<j)]+1;
    for(int k=0;k<n;k++)
        if(((1<<k)&i)&&k!=j)
            f[i]=min(f[i],f[(i&g[j][k])^i]+1);
}

 洛谷P3052 [USACO12MAR]摩天大樓裏的奶牛Cows in a Skyscraper

f 數組爲結構體

f[S].cnt 表示集合 S 最少的分組數

f[S].v 表示集合 S 最少分組數下當前組所用的最少容量

f[S] = min(f[S], f[S - i] + a[i]) (i ∈ S)

運算重載一下便可。

 洛谷P2915 [USACO08NOV]奶牛混合起來Mixed Up Cows

比較簡單的一個題

f[sta][i]表示選了的牛的集合爲sta,且最後一個牛是i的混亂序列方案數,正着推就行

if(sta&(1<<(i-1)))
    for(int j=1;j<=n;j++)
        if(abs(a[j]-a[i])>k)f[sta][i]+=f[sta^(1<<(i-1))][j];

 洛谷P3092 [USACO13NOV]沒有找零No Change

由於是順序購買,因此狀態s的答案有關的是使用順序。以後就有狀態f[s]表示用狀態s最多可買多少物品。最後在算狀態最大值的時候要套一個二分。

 

動規求方案數

洛谷P1310 表達式的值

注意判斷分類和分步,分類加法,分步乘法。這種表達式上的問題多要用到棧,而對於括號裏的計算多用遞歸就求解

這道題能夠用DP求解,設f(s,0)爲s=0的方案數,f(s,1)爲s爲1的方案數,則
f(a+b,0)=f(a,0)*f(b,0);
f(a+b,1)=f(a,0)*f(b,1)+f(a,1)*f(b,0)+f(a,1)*f(b,1);
f(a*b,0)=f(a,0)*f(b,0)+f(a,1)*f(b,0)+f(a,0)*f(b,1);
f(a*b,1)=f(a,1)*f(b,1)。
接下來就是一個相似於樹形DP的過程了。在這裏DP的是表達式樹。

在dp中有個很著名的恆等變換(手動滑稽),就是 求最值=判斷最值的可行性
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息