动态规划

广泛用于求解组合优化问题
使用这种技术的算法,不是递归调用自身,但是问题的基础解通常是用递归函数的形式来说明的。

最长公共子序列问题

问题描述

有两个长度分别为n和m的字符串A和B,确定A和B中最长公共子序列的长度。这里, A = a 1 a 2 ⋯ a n A = a_1a_2\cdots a_n A=a1a2an的子序列是一个形式为 a i 1 a i 2 . . . a i k a_{i_1}a_{i_2}...a_{i_k} ai1ai2...aik的字符串,其中每个 i j i_j ij都在1和n之间,并且 1 ≤ i 1 &lt; i 1 &lt; ⋯ &lt; i k ≤ n 1\leq i_1&lt;i_1&lt;\cdots &lt;i_k\leq n 1i1<i1<<ikn。假设A=zxyxyz,B=xyyzx,很很显最长公共子序列为xyyz。

解决思路

为了使用动态规划技术,首先寻找一个递推公式,令 A = a 1 a 2 ⋯ a n , B = b 1 b 2 ⋯ b m A = a_1a_2\cdots a_n, B = b_1b_2\cdots b_m A=a1a2an,B=b1b2bm,令 L [ i . , j ] L[i.,j] L[i.,j]表示 a 1 a 2 ⋯ a i a_1a_2\cdots a_i a1a2ai b 1 b 2 ⋯ b j b_1b_2\cdots b_j b1b2bj的最长公共子序列的长度。注意, i i i j j j可能是0,此时 L = 0 L = 0 L=0
下面给出递推式
L [ i , j ] = { 0 if  i = 0  or  j = 0 L [ i − 1 , j − 1 ] + 1 if  i &gt; 0 , j &gt; 0  and  a i = b j m a x { L [ i , j − 1 ] , L [ i − 1 , j ] } if  i &gt; 0 , j &gt; 0  and  a i ≠ b j L[i, j] = \begin{cases} 0 &amp; \text {if $i= 0$ or $ j=0$} \\ L[i-1,j-1]+1&amp;\text{if $ i&gt;0,j&gt;0$ and $a_i = b_j$}\\ max\lbrace L[i,j-1],L[i-1,j]\rbrace &amp; \text{if $i&gt;0,j&gt;0$ and $a_i\neq b_j$} \end{cases} L[i,j]=0L[i1,j1]+1max{L[i,j1],L[i1,j]}if i=0 or j=0if i>0,j>0 and ai=bjif i>0,j>0 and ai̸=bj

算法

对于每一对i和j的值, 0 ≤ i ≤ n , 0 ≤ j ≤ m 0\leq i\leq n,0\leq j\leq m 0in,0jm,用一个 ( n + 1 ) ∗ ( m + 1 ) (n+1)*(m+1) (n+1)(m+1)大小的表来计算存储 L [ i , j ] L[i,j] L[i,j]的值

\\算法LCS
\\输入:字符串A和B,长度分别为n和m
\\输出:A和B最长公共子序列的长度
main(){
	for i = 0:n
		L[i,0] = 0
	for j = 0:m
		L[0,j] = 0
	for i = 1:n
		for j = 1:m
			if a[i] = b[j]
				L[i,j] = L[i-1,j-1] + 1
			else
				L[i,j] = max{L[i,j-1],L[i-1,j]}
	return L[n, m]
}

0/1背包问题

问题描述

U = { u 1 , u 2 , ⋯ &ThinSpace; , u n } U=\lbrace u_1,u_2, \cdots ,u_n\rbrace U={u1,u2,,un}是一个准备放入容量为 C C C的背包中的 n n n项物品的集合。对于 1 ≤ j ≤ n 1\leq j \leq n 1jn,令 s j s_j sj v j v_j vj分别为第 j j j项物品的体积和价值,这里, C , s j , v j C,s_j,v_j C,sj,vj j j j都是正整数。我们要解决的问题四用U中的一些物品来装满背包,这些物品的总体积不超过 C C C,然而要使他们的总价值最大。

解决思路

V [ i , j ] V[i,j] V[i,j]表示从前 i i i { u 1 , u 2 , ⋯ &ThinSpace; , u i } \lbrace u_1,u_2, \cdots ,u_i\rbrace {u1,u2,,ui}中取出来的装入体积为 j j j的背包的物品的最大价值。这里, 0 ≤ i ≤ n 0\leq i\leq n 0in 0 ≤ j ≤ C 0\leq j \leq C 0jC。找出递推式
V [ i , j ] = { 0 if  i = 0  or  j = 0 V [ i − 1 , j ] if  j &lt; s i m a x { V [ i − 1 , j ] , V [ i − 1 , j − s i ] + v i } if  i &gt; 0 , j ≥ s i V[i, j] = \begin{cases} 0 &amp; \text {if $i= 0$ or $ j=0$} \\ V[i-1,j]&amp;\text{if $ j&lt;s_i$}\\ max\lbrace V[i-1,j],V[i-1,j-s_i]+v_i\rbrace &amp; \text{if $i&gt;0,j\geq s_i$} \end{cases} V[i,j]=0V[i1,j]max{V[i1,j],V[i1,jsi]+vi}if i=0 or j=0if j<siif i>0,jsi

算法

\\算法KNAPSACK
\\输入:物品集合U={u1, u2, ..., un},体积分别为s1, s2,...,sn,价值分别为v1, v2, v3,..., vn,容量为C的背包
\\输出:满足条件的最大总价值
main(){
	for i = 0:n
		V[i,0] = 0
	for j = 0:C
		V[0,j] = 0
	for i = 1:n
		for j = 1:C
			if s[i] <= j
				V[i,j] = max{v[i,j],L[i-1,j-si]+vi}
			else
				V[i,j] = V[i-1,j]
	return V[n, C]
}

总结

很明显,无论是公共子序列还是背包问题,最关键的就是能找到合适的递推公式。说白了,还是要数学直觉强,加上见多识广。数学才是王道啊!tt,你说是不是。

卡片计分——附加题

问题描述

小a和小b玩一个游戏,有n张卡牌,每张上面有两个正整数x,y。取一张牌时,个人积分增加x,团队积分增加y。求小a,小b各取若干张牌,使得他们的个人积分相等,且团队积分最大。

输入描述

第一行n,接下来n行,每行两个正整数x,y

输出描述

一个整数,表示团队积分的最大值

示例

输入:
4
3 1
2 2
1 4
1 4
输出:
10

数据范围

0 < n < 100
0 < x < 1000
0 < y < 1e6

算法

很难想象这道题竟然也能用动态规划,数学果然令人畏惧!我当然也是看了网上的才懂。以 L [ i , j ] L[i,j] L[i,j]表示前 i i i张牌中,个人积分相差 j j j时,获得的最大团队积分, 0 ≤ i ≤ n , 0 ≤ j ≤ x m a x 0\leq i\leq n,0\leq j\leq x_{max} 0in,0jxmax x x x数组表示个人积分, y y y数组表示团队积分。这里给出递推式,注意:个人认为网上的程序(就是 t 1 , t 2 t1,t2 t1,t2条件)有一点问题,因为要考虑$L[i-1][j-x[i-1]] 是 否 大 于 0 , 即 如 果 等 于 0 , 那 么 就 无 法 组 成 前 是否大于0,即如果等于0,那么就无法组成前 00i 张 牌 中 , 个 人 积 分 相 差 张牌中,个人积分相差 j-x[i-1] 时 的 情 况 , 此 时 时的情况,此时 t1 只 能 等 于 0 ; 只能等于0; 0j < x[i-1] 也 要 考 虑 ; 还 有 就 是 积 分 相 差 为 0 的 情 况 , 因 为 此 情 况 必 然 存 在 , 即 两 边 都 不 拿 牌 , 也 就 是 也要考虑;还有就是积分相差为0的情况,因为此情况必然存在,即两边都不拿牌,也就是 0j == x[i-1]的特殊情况。
L [ i , j ] = { 0 if  i = 0 m a x { L [ i − 1 , j ] , t 1 , t 2 } if  i &gt; 0 L[i, j] = \begin{cases} 0 &amp; \text {if $i= 0$} \\ max\lbrace L[i-1,j],t1,t2\rbrace &amp; \text{if $i&gt;0$} \end{cases} L[i,j]={0max{L[i1,j],t1,t2}if i=0if i>0
t 1 = { 0 others L [ i − 1 ] [ d i f f ] + y [ i − 1 ]  if  L [ i − 1 ] [ d i f f ] &gt; 0  or  d i f f = = 0 ) d i f f = a b s ( j − x [ i − 1 ] ) t1 = \begin{cases} 0 &amp; \text {others} \\ L[i-1][diff] + y[i-1] &amp;\text{ if $L[i-1][diff] &gt; 0$ or $diff== 0)$} \end{cases} diff = abs(j - x[i-1]) t1={0L[i1][diff]+y[i1]others if L[i1][diff]>0 or diff==0)diff=abs(jx[i1])
t 2 = { 0 others L [ i − 1 ] [ j + x [ i − 1 ] ] + y [ i − 1 ] if  j + x [ i − 1 ] ≤ x m a x  and  L [ i − 1 ] [ j + x [ i − 1 ] ] &gt; 0 t2 = \begin{cases} 0 &amp; \text {others} \\ L[i-1][j+x[i-1]] + y[i-1] &amp;\text{if $j+x[i-1] \leq x_{max}$ and $L[i-1][j+x[i-1]] &gt; 0$} \end{cases} t2={0L[i1][j+x[i1]]+y[i1]othersif j+x[i1]xmax and L[i1][j+x[i1]]>0

程序

//c++ 源码
#include<iostream> 
#include<vector>
using namespace std;
/*
4
3 1
2 2
1 4
1 4
如果使用网上的一些程序,第二个样例会失败
4
3 1
4 2
5 3
10 4
*/
int max3(int x, int y, int z){
	if(x < y)
		x = y;
	if(x < z)
		x = z;
	return x;
}
int main(){
	int n;
	cin >> n;
	int *x = new int[n];
	int *y = new int[n];
	int max = 0;
	vector<vector<int> > help;
	for(int i = 0; i < n; ++ i){
		cin >> x[i] >> y[i];
		if(x[i] > max)
			max = x[i];
	}
	for(int i = 0; i < n + 1; ++ i){
		vector<int> temp(max + 1, 0);
		help.push_back(temp);
	}
	for(int i = 1; i < n + 1; ++ i){
		printf("%d ",x[i-1]);
		for(int j = 0; j < max + 1; ++ j){
			int temp1 = 0, temp2 = 0;
			int diff = j - x[i-1];
			if (diff < 0) 
				diff = 0 - diff;
			if((help[i-1][diff] > 0 || diff == 0)
			//注意,就是条件这里不太一样
				temp1 = help[i-1][diff] + y[i-1];
			if( j + x[i-1] <= max && help[i-1][j+x[i-1]] > 0)
				temp2 = help[i-1][j+x[i-1]] + y[i-1];
			help[i][j] = max3(help[i - 1][j], temp1, temp2);
			printf("%d  ",help[i][j]);	
		}
		printf("\n");
	}
	cout << help[n][0] << endl;
	delete []x;
	delete []y;
	
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值