HDUOJ1421(DP)
来源:互联网 发布:淘宝客佣金5元怎么设置 编辑:程序博客网 时间:2024/06/08 02:44
ACM1421
又是动态规划的题目,dp方面问题对现阶段的自己还是一个坎,还是要多理解多刷题。
首先从简单的数塔入手,从第一行开始走,走到最后一行,怎么使经过的值之和最大?
dp问题的核心:找到状态并建立状态转移方程
首先需要建立一个数塔模型,模型中的元素用a(i,j)(j<=i)表示,如第三行第二个元素10用a(3,2)表示。并且我们规定从a(i,j)往下走所能得到的最大值为dp(i,j)。
我们以数塔中任意一个元素a(i,j)为一个状态,根据规则,该元素可以选择向左下方a(i+1,j)或者右下方a(i+1,j+1)移动(最后一行除外),显然对于处于a(i,j)一定选择{dp(i+1,j),dp(i+1,j+1)}中较大的值,于是我们得到了dp(i,j)的计算公式:dp(i,j)=a(i,j)+max(dp(i+1,j),dp(i+1,j+1)),这就是我们所要找的状态转移方程。很容易就知道该问题的最终答案是dp(1,1)。
接下来介绍三种方法来求解:
1.递归求解
int dp(int i,int j){ if(i!=n)return a[i][j]+=max(dp(i+1,j),dp(i+1,j+1)); else return 0; //n表示最后一行的行数,即为边界}
分析该算法,会发现效率很低。例如对于上图dp(3,2),dp(4,2),dp(4,3)将会被重复计算,但是这部分的结果一样,却被重复计算,我们就要设法使这部分不被重复,很显然,要使他不重复,那就在第一次计算的时候把结果记录下来,下次遇到的时候直接使用,就避免了重复计算。
2.根据算法1的分析,我们得到了带记忆功能的算法。
int dp(int i,int j){//第一行就是所说的记忆功能,如果dp>=0,就表明它已经被计算过了,可以直接使用,//所以在函数之前要将dp的值全赋值成-1,表示dp都未被计算过memset(dp,-1,sizeof(dp)) if(dp(i,j)>=0)return dp(i,j); else return dp[i][j]=a[i][j]+max(dp(i+1,j),dp(i+1,j+1));}
从图中很直观的看出记忆功能的算法能直接利用计算过的结果。
3.递推算法
算法3改变了思路,前两种都是自顶向下计算,算法3则选择从下向上计算,而且规定一下是从左下角向顶角计算。
for(int i=n;i>=1;i--) for(int j=1;j<=i;j++) dp[i][j]=a[i][j]+max(a[i-1][j],a[i-1][j+1]);//仔细分析甚至连dp数组都可以不用 a[i][j]+=max(a[i-1][j],a[i-1][j+1]);
下面给出数塔问题ACM2084的完整代码,这里选择算法3
#include<iostream>#include<algorithm>#define MAXN 105int a[MAXN][MAXN];using namespace std;int main(){ int t; cin >> t; while (t--) { int n; cin >> n; for (int i = 1; i <= n; i++) for (int j = 1; j <= i; j++) cin >> a[i][j]; for (int i = n - 1; i >= 1; i--) for (int j = 1; j <= i; j++) a[i][j] += max(a[i + 1][j], a[i + 1][j + 1]); cout << a[1][1] << endl; } return 0;}
接下来分析一下开头的ACM1421
首先必须承认渣渣的我这道题想了很久,也是借鉴了别人的思路。
先申明用a[n]来存储输入的数据,用dp[i][j]表示从i个数中取j对时的最小值,例如dp[3][1]表示从3个数取1对的最小疲劳度(当然三个数的时候也只能取一对)
我想从题意中应该很容易想到给数组a进行排序
sort(a,a+n);
我们先从简单的找找规律:
当只有两个数的时候,结果显然等于(a[2]-a[1])*(a[2]-a[1]),根据前面的定义我们把结果记作dp[2][1];
当有三个数,要么a[1]和a[2],要么a[2]和a[3]组合,我们取两个组合的最小值,因此:
dp[3][1]=min(dp[2][1],(a[3]-a[2])*(a[3]-a[2]));
我们接着在推一个当四个数的时候,此时我们可以选取一对或者两对,
此时选取一对可以分成两类:
1.不包括第四个数时,dp[4][1]=dp[3][1];2.包括第四个数时,dp[4][1]=(a[4]-a[3])*(a[4]-a[3]);
选取两对:
dp[4][2]=dp[2][1]+(a[4]-a[3])*(a[4]-a[3]);
不知道你们发现什么没(如果还没有,可以在推一两组找找规律)
dp[i][j]=min(dp[i-1][j],dp[i-2][j-1]+(a[i]-a[i-1])*(a[i]-a[i-1]);//其实这就是我们要找的状态转移方程//假设要找i个元素中j对的最小值:要么从前i-1个元素中找j对;要么从前i-2个元素中找j-1对+a[i]-a[i-1])*(a[i]-a[i-1];
既然找到了状态转移方程,我们就根据题意进行一些细节的处理
#include<iostream>#include<algorithm>#define MAXN 2050int dp[MAXN][MAXN];int a[MAXN];using namespace std;int main(){ int n, k; while (cin >> n >> k) { for (int i = 1; i <=n; i++) cin >> a[i]; sort(a+1, a + n+1); memset(dp, 0, sizeof(dp)); for (int i = 1; i <= n; i++) for (int j = 1; j <= k; j++) dp[i][j] = 268435456; for (int i = 2; i <= n; i++) for (int j = 1; 2*j<= i ; j++) dp[i][j] = min(dp[i - 2][j - 1] + (a[i] - a[i - 1])*(a[i] - a[i - 1]), dp[i - 1][j]); cout << dp[n][k]<<endl; } return 0;}
- HDUOJ1421(DP)
- hduoj1421
- dp
- dp
- dp
- 【DP】
- dp
- dp
- DP
- DP
- DP
- DP
- DP
- dp
- DP
- dp
- DP
- DP
- angularjs路由菜单如何强制刷新?
- ReentrantLock实现原理详解
- 发布web项目到linux服务器下
- 红粉NBA:那些在联盟中的跨国恋情
- C语言练习题(5)
- HDUOJ1421(DP)
- 【示例教程】LEADTOOLS中如何通过OCR识别获取每一行文本
- 2016China Final 二分 UVALive7900(D)
- java 打印
- oracle数据库使用时间作为查询条件时的写法
- Linex新手常用命令
- MySQL高可用在网易的最佳应用与实践
- 关于网站扫描到的几种漏洞及处理办法
- HDU