秋季學期一起開心講課-week05-dp
阿新 • • 發佈:2018-11-25
1.初步動態規劃:
①:數字三角形問題:
有形如下圖所示的數塔,從頂部出發,在每一結點可以選擇向左走或是向右走,一起走到底層,要求找出一條路徑,使路徑上的值最大。
樣例輸入:
5
13
11 8
12 7 26
6 14 15 8
12 7 13 24 11
樣例輸出:
86(13->8->26->15->24)
分析:因為從上面往下走,必須要知道左右兩邊那條路更大。所以要從底層開始層層遞進,直至找到所標記的點。
dp[i][j] = max(dp[i+1][j],dp[i+1][j+1])+a[i][j];
#include<iostream> #include<cstring> #include<algorithm> #define maxn 10000 int a[maxn][maxn]; int dp[maxn][maxn]; using namespace std; int main() { int n; scanf("%d",&n); for(int i = 0; i < n; i ++) for(int j = 0; j <= i; j++) scanf("%d",&a[i][j]); for(int i = 0; i < n; i ++) dp[n-1][i] = a[n-1][i]; for(int i = n-2; i >= 0; i--) for(int j = 0;j <= i; j ++) dp[i][j] = max(dp[i+1][j],dp[i+1][j+1]) + a[i][j]; printf("%d\n",dp[0][0]); return 0; }
2.序列dp
1)最長上升子序列LIS
輸入n及一個長度為n的數列,求出此序列的最長上升子序列長度。上升子序列指的是對於任意的i<j都滿足ai<aj的子序列。(1<=n<=1000,0<=ai<=1000000)
樣例輸入:
5
4 2 3 1 5
樣例輸出:
3(最長上升子序列為2, 3, 5)
#include<iostream> #include<algorithm> using namespace std; #define maxn 1005 int a[maxn]; int dp[maxn]; int main() { int n; scanf("%d",&n); for(int i = 1; i <= n; i ++) { scanf("%d",&a[i]); dp[i] = 1; } int ans = 1; for(int i = 1; i <= n; i ++) { for(int j = 1; j < i; j ++) { if(a[j]<a[i]) dp[i] = max(dp[i],dp[j]+1); } ans = max(ans,dp[i]); } printf("%d\n",ans); return 0; }
3.字串的最長公共子序列和最大公共子串
區別:
例如:acdea
acddda
最長公共子序列為4(“acda”)
最大公共子串為3(“acd”)
1>.最長公共子序列
動態方程:dp[i+1][j+1] = dp[i][j] + 1;
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1005
int dp[maxn][maxn];
int main()
{
char s1[maxn],s2[maxn];
while(~scanf("%s",s1))
{
scanf("%s",s2);
int len1 = strlen(s1);
int len2 = strlen(s2);
memset(dp,0,sizeof(dp));
for(int i = 0; i < len1; i ++)
for(int j = 0; j < len2; j ++)
{
if(s1[i]==s2[j])
dp[i+1][j+1] = dp[i][j] + 1;
else
dp[i+1][j+1] = max(dp[i+1][j],dp[i][j+1]);
}
printf("%d\n",dp[len1][len2]);
}
}
2>最大公共子序列
在情況1的基礎上稍加改動
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1005
int dp[maxn][maxn];
int main()
{
char s1[maxn],s2[maxn];
while(~scanf("%s",s1))
{
scanf("%s",s2);
int len1 = strlen(s1);
int len2 = strlen(s2);
memset(dp,0,sizeof(dp));
int ans = 0;
for(int i = 0; i < len1; i ++)
for(int j = 0; j < len2; j ++)
{
if(s1[i]==s2[j])
dp[i+1][j+1] = dp[i][j] + 1;
else
dp[i+1][j+1] = max(dp[i+1][j],dp[i][j+1]);
ans = max(ans,dp[i+1][j+1]);
}
printf("%d\n",dp[len1][len2]);
}
}
2.揹包問題:
1>01揹包 有n件物品,揹包總量為m,給出n種物品的價格和重量,求出最優解。
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 5001
using namespace std;
int a[maxn],b[maxn];
int dp[maxn];
int main()
{
int n,m;
scanf("%d %d",&n,&m);
for(int i = 1; i <= n ; i ++)
scanf("%d %d",&a[i],&b[i]);//價值為a,重量為b
for(int i = 1; i <= n; i ++)
for(int j = m; j >= b[i] ; j--)
{
dp[j] = max(dp[j],dp[j-b[i]]+a[i]);
}
printf("%d\n",dp[m]);
}
完全揹包
有N種物品和容量為M的包,其中N種物品有無數件,並給出N種物品的價值和重量。
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 5001
using namespace std;
int a[maxn],b[maxn];
int dp[maxn];
#define INF 0x3f3f3f3f
int main()
{
int n,m;
scanf("%d %d",&n,&m);
memset(dp,INF,sizeof(dp));
for(int i = 1; i <= n ; i ++)
scanf("%d %d",&a[i],&b[i]);//價值為a,重量為b
for(int i = 1; i <= n; i ++)
for(int j = b[i]; j <= m; j ++)
{
dp[j] = max(dp[j],dp[j-b[i]]+a[i]);
}
if(dp[m]==INF)
printf("NO\n");
else
printf("%d\n",dp[m]);
}
多重揹包
有一個容量為V的揹包和N件物品,第i件物品最多有Num[i]件,每件物品的重量是weight[i],收益是cost[i]。
#include <iostream>
using namespace std;
const int N = 3;//物品個數
const int V = 8;//揹包容量
int Weight[N + 1] = {0,1,2,2};
int Value[N + 1] = {0,6,10,20};
int Num[N + 1] = {0,10,5,2};
int f[V + 1] = {0};
void ZeroOnePack(int nWeight,int nValue)
{
for (int v = V;v >= nWeight;v--)
{
f[v] = max(f[v],f[v - nWeight] + nValue);
}
}
void CompletePack(int nWeight,int nValue)
{
for (int v = nWeight;v <= V;v++)
{
f[v] = max(f[v],f[v - nWeight] + nValue);
}
}
int MultiKnapsack()
{
int k = 1;
int nCount = 0;
for (int i = 1;i <= N;i++)
{
if (Weight[i] * Num[i] >= V)
{
//完全揹包:該類物品原則上是無限供應,
//此時滿足條件Weight[i] * Num[i] >= V時,
//表示無限量供應,直到揹包放不下為止.
CompletePack(Weight[i],Value[i]);
}
else
{
k = 1;
nCount = Num[i];
while(k <= nCount)
{
ZeroOnePack(k * Weight[i],k * Value[i]);
nCount -= k;
k *= 2;
}
ZeroOnePack(nCount * Weight[i],nCount * Value[i]);
}
}
return f[V];
}
int main()
{
cout<<MultiKnapsack()<<endl;
return 0
}