洛谷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
區間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]); }
/* 輸入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
像這種要連續選擇的問題能夠考慮用前綴和維護
乘法原理,設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
洛谷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]
數據範圍很小,考慮狀態壓縮,把打死的豬的集合壓縮成二進制
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最多可買多少物品。最後在算狀態最大值的時候要套一個二分。
注意判斷分類和分步,分類加法,分步乘法。這種表達式上的問題多要用到棧,而對於括號裏的計算多用遞歸就求解
這道題能夠用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的是表達式樹。