ACM近期之dp学习总结

一.对dp解题的认识

I.理解

初学dp,经过这几天的对于dp的学习,我的简单理解是:
1.做dp的问题首先要搞清楚题意,将一个看似繁琐复杂的问题提取出关键意思转化成可以用简单语言表示的数学题。
2.对于dp动态规划来说,状态转移方程是整个代码的核心,要学会将一个大问题分解成若干个子问题,确定好前后问题联系的状态,进而写出状态转移方程,此外,还要找出边界条件。利用这两点,便构成了解决问题的代码思路。

II.进步

还记得前几天我看这部分的题时一点思路没有,后来这几天通过多看别人的代码,发现对于0-1背包问题用的比较广泛,就总结出了这一大类问题的共同解法。其实,前几天做题感觉做题十分困难只是由于没有找到这一类问题的实质以及共通之处而已。此外,因为我一开始对0-1背包问题的实质不太了解,便运用了动态调试运行代码的方法,通过一步步的输出,从而具体了解代码是如何运行的。同时,我发现动态调试程序不仅可以让你在代码过程出现错误时找出错误,还可以让你在代码正确时更加直观的明白代码是一步步如何运行的。

III.用dp写代码的好处
用dp的方法写代码可以使代码简洁很多,但难点是要找出状态转移方程

二.dp一类问题(0-1背包问题)的题型解决方法总结

I. 0-1背包问题模型
有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 weight[i],价值是 value[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

0-1背包问题特点是:表示的是每个物品只有一件,每件物品不能分割,在不超过背包容量的同时,如何选取物品,使得背包所装的价值最大(背包可以装不满)

II.状态转移方程

状态转移方程是:f[i][v]=max{f[i-1][v],f[i-1][v-weight[i]]+value[i]}

解释一下上面的方程:“将前i件物品放入容量为v的背包中”这个子问题,如果只考虑第i件物品放或者不放,那么就可以转化为只涉及前i-1件物品的问题:
即 1、如果不放第i件物品,则问题转化为“前i-1件物品放入容量为v的背包中”;
2、如果放第i件物品,则问题转化为“前i-1件物品放入剩下的容量为v-weight[i]的背包中”,此时能获得的最大价值就是f [i-1][v-weight[i]]再加上通过放入第i件物品获得的价值value[i]。
则f[i][v]的值就是1、2中最大的那个值

III.例题

1.题意:Roy想要抢劫银行,每家银行多有一定的金额和被抓到的概率,知道Roy被抓的最大概率P,求Roy在被抓的情况下,抢劫最多。
Sample Input

3
0.04 3
1 0.02
2 0.03
3 0.05
0.06 3
2 0.03
2 0.03
3 0.05
0.10 3
1 0.03
2 0.02
3 0.05

Sample Output

2
4
6

思路
方程是:f[j]=max(f[j],f[j-q[i].money]*q[i].v) 其中,f[j]表示抢金额为j的最大的逃脱概率,条件是f[j-q[i].money]可达,也就是之前抢劫过;
始化为:f[0]=1,其余初始化为-1 (抢0块钱肯定不被抓,逃脱概率为1)

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
double dp[10000+5];
double p[105];
int w[105];
int main()
{
    int T;
	scanf("%d",&T);
	while(T--)
	{
		memset(dp,0,sizeof(dp));
		int n,sum=0;
		double maxp;
		scanf("%lf%d",&maxp,&n);
		for(int i = 1;i<=n;i++)
			scanf("%d%lf",&w[i],&p[i]),sum+=w[i];
		dp[0]=1;
		for(int i = 1;i<=n;i++)
			for(int j = sum;j>=w[i];j--)
				dp[j]=max(dp[j],dp[j-w[i]]*(1-p[i]));
		int ans = 0;
		for(int j = 0;j<=sum;j++)
			if((1-dp[j]) -maxp<1e-6 )
				ans = max(ans,j);
		printf("%d\n",ans);
	}
   return 0;
}

其中,上面体现0-1背包思想的核心代码为

		for(int i = 1;i<=n;i++)
			for(int j = sum;j>=w[i];j--)
				dp[j]=max(dp[j],dp[j-w[i]]*(1-p[i]));

此外,如果我们对代码的具体实现不太清楚的话,可以通过动态调试运行来理解
动态调试运行情况
ACM近期之dp学习总结以此题为例讲述了0-1背包的用法,其他同类型的题总结成数学问题都是同一本质用法,在此不再列举。

三.闲碎不熟知识点总结

1.通常将无穷大设置为INF=0x3f3f3f3f
2.将数列按从大到小排序除了用函数表示,还可以用#define max(a,b) a>b?a:b语句表示