1. 程式人生 > >秋季學期一起開心講課-week05-dp

秋季學期一起開心講課-week05-dp

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
}