经典DP
1.背包問(wèn)題
(1)01背包
從n個(gè)重量和價(jià)值分別為wi,vi的物品,從中選出不超過(guò)W的物品,每種物品僅有一件,求所有方案中V的最大值。
最樸素最簡(jiǎn)單也最費(fèi)時(shí)的方法:O(2^n) int rec(int i,int j)//從第i個(gè)開始挑選總重小于j的部分
遞歸 ?遞歸終止條件:i==n ?return 0;
??????遞歸分支:① j<wi res=rec(i+1,j); ?//無(wú)法挑選,看下一個(gè)
????????????????② res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]))//挑選,不挑選,選其中大的
分析:遞歸搜索深度→n,每層兩次分支(選與不選),有重復(fù)計(jì)算
優(yōu)化:記錄每次遞歸的結(jié)果(記憶化搜索)
?
Int dp[MAX_N][MAX_N];
遞歸 int rec(int i,int j)
????初始條件:memset(dp,-1,sizeof(dp));
終止條件:①dp[i][j]>=0 returndp[i][j]//已計(jì)算過(guò)
②i==n return 0;
????遞歸分支:① j<wi res=rec(i+1,j); ?//無(wú)法挑選,看下一個(gè)
????????????????② res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]))//挑選,不挑選,選其中大的
分析及remark:復(fù)雜度O(nW)
???數(shù)組初始化:①memset(type *arrary,figure,sizeof(arrary))
? ? ? ? ? ? ? ? ? ? ? 只能填充(0,+-1,0x3f3f3f3f),其他值不可以?
? ? ? ? ? ? ? ? ? ? ? memset按照1字節(jié)為單位對(duì)內(nèi)存填充,-1的二進(jìn)制每一位均為1
? ? ? ? ? ? ? ? ? ? ? ②fill(type* arrary,type *arrary+n,figure) 可賦值任意值
????遞歸 ?
?↓↓↓↓↓↓ ? ? ? ? ? ? ? ?? for(int i=n-1;i>=0;i--)?//
遞推(雙重循環(huán)) ??????????for(int j=0;j<=w;j++)
? ? ? ? ? ? ? ? ? ? ? ? ? { if(j<w[i]) dp[i][j]=dp[i+1][j]; //不選
?????????????????????????else dp[i][j]=max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]);}
其他3種遞推寫法:(來(lái)源:《挑戰(zhàn)程序設(shè)計(jì)競(jìng)賽》)
?
(2)完全背包
遞推關(guān)系1:(3重循環(huán),有重復(fù)計(jì)算)復(fù)雜度O(nW^2)
For(int i=0;i<n;i++)
?For(int j=0;j<=w;j++)
??For(int k=0;k*w[i]<=j;k++)
? Dp[i+1][j]=max(dp[i+1][j],dp[i+1][j-k*w[i]]+k*v[i])
優(yōu)化:(左上→右下 ?變?yōu)?左→右)
Dp數(shù)組初始化為0
For(int i=0;i<n;i++)
?For(int j=0;j<=w;j++)
{
?If(j<w[i]) dp[i+1][j]=dp[i][j];
?Else ?dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i])
}???????????只有這里與01背包不同,前j個(gè)已更新過(guò),可直接用
?
進(jìn)一步優(yōu)化:用一個(gè)數(shù)組實(shí)現(xiàn),只需要記錄當(dāng)前最優(yōu)狀態(tài)
比較01背包與完全背包(循環(huán)方向不同)
?
2.LCS(Longest common subsequence)
?
dp[n][m]即為所求
for(int i=0;i<n;i++)
??for(int j=0;j<m;j++)
{
??if(s[i]==t[i])
???dp[i+1][j+1]=dp[i][j]+1;
else
???dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1])
}
?
3.LIS(Longest Increasing subsequence)
?
dp[i]:以ai為結(jié)尾的最長(zhǎng)上升子序列長(zhǎng)度
dp[i]=max{1,dp[i]+1|j<i且aj<ai}
O(n^2)
int res;
for(int i=0;i<n;i++)
?{
? dp[i]=1;
for(int j=0;j<i;j++)
? if(a[j]<a[i]) ? ? ? ? ? ?? //每存在aj<ai&&j<i,dp[i]更新一次
?? dp[i]=max{dp[i],dp[j]+1};
}
res=max{dp[i]|0<=i<n}
Remark:
其他方法:
可以用lower_bound();
?? dp[i]:長(zhǎng)度為i+1的上升子列中末尾元素的最小值(不存在的話為inf)
dp[max_n]初始化為inf,按順序逐個(gè)考慮數(shù)列的元素,對(duì)于每個(gè)ai,如果i=0||dp[i-1]<ai,就用dp[i]=min(dp[i],ai)更新,最終找出使得dp[i]<inf的最大的i+1即為結(jié)果。DP直接實(shí)現(xiàn),可以在O(n^2)的時(shí)間內(nèi)給出結(jié)果,但可以進(jìn)一步優(yōu)化,dp數(shù)組中除inf之外是單調(diào)遞增的,對(duì)于每個(gè)ai最多有一次更新,更新的位置可用二分的方法優(yōu)化,時(shí)間復(fù)雜度可以降低到O(nlogn)
int dp[max_n]
void solve()
{
? fill(dp,dp+n,inf);
? for(int i=0;i<n;i++)
? ? *lower_bound(dp,dp+n,a[i])=a[i];
? res=lower_bound(dp,dp+n,inf)-dp;
}
// lower_bound()可以從已排好序的a中利用二分搜索找出滿足ai>=k的ai的最小的指針,類似的還有upper_bound,找出的為ai>k的最小指針
//求n個(gè)有序數(shù)組a中k的個(gè)數(shù),可以用:upper_bound(a,a+n,k)-lower_bound(a,a+n,k);
轉(zhuǎn)載于:https://www.cnblogs.com/Egoist-/p/7391224.html
總結(jié)
- 上一篇: java 开源框架集
- 下一篇: 实施工程师的发展前景