动态规划DP算法简单应用(简单0-1背包问题,以空间换时间思想)(入门题*2)

题目来源:NUISTOJ

P1439 背包九讲(1):简单的0-1背包

题目描述:

有一个箱子容量为 V(正整数,0<=V<=20000),同时有 n 个物品(0<n<=30),每个物品有一定的体积和价值。要求 n 个物品中,任取若干个装入箱内,在箱子能放得下的前提下,满足箱子内部的价值最大。

输入描述:

一个整数 v,表示箱子容量

一个整数 n,表示有 n 个物品

接下来 n 个整数,分别表示这 n 个物品的各自体积和价值

输出描述:

一个整数,表示箱子能装下的最大价值。

样例输入:

3

2

2 100

4 200

样例输出:

100

#include<iostream>
using namespace std;
int dpfun(int num, int capacity, int volume[], int value[]){
	//num(物品序号)小于0说明没有物品,无法再放 
	if(num<0) return 0;
	//如果放入当前物品,会导致箱子容量小于0,则不放入当前物品(直接进入下一层递归) 
	if(capacity-volume[num]<0) return dpfun(num-1, capacity, volume, value); 
	//如果有足够容量来放入当前物品,则判断:
	//1、不放入当前物品(直接进入下一层递归);2、放入当前物品(减容量,加价值,进入下一层递归) 
	//1与2递归的结果取最大值 
	else return max(dpfun(num-1, capacity, volume, value), value[num]+dpfun(num-1, capacity-volume[num], volume, value));
}
int main()
{
	int V, n, volume[50], value[50];
	while(cin>>V>>n){
		//物品序号从0~n-1 
		for(int i=0;i<n;i++)
			cin>>volume[i]>>value[i];
		cout<<dpfun(n-1, V, volume, value)<<endl;//此处物品序号从n-1开始 
	}
	return 0;
}

P1236 夺取宝藏

题目描述:

Ipomy 现在来到了阿兹特克宝藏堆中。这些宝藏散落放在一个 m * n 的网格上,每个宝藏都有一个价值。Ipomy 自然是希望将所有宝藏统统拿走,但他在走出迷宫时,不小心中了魔咒,一次只能向下或向右移动一步。假设 Ipomy 身处网格的左上角,而古城的出口在右下角,他想在离开古城前,拿到价值之和尽可能大的宝藏。请你编写程序,帮助他计算他可以拿到的最大价值之和。

输入描述:

多组输入。

每组输入的第一行为两个整数 m 和 n,用来描述网格的规格。保证 1 <= m, n <= 1000。

接下来的 m 行,每行 n 个整数,表示每个格子上面的宝藏的价值。输入数据保证 Ipomy 起始所在处没有宝藏,即价值为 0,以及每个宝藏的价值均在 int 型的表示范围内。

输出描述:

多组输出。

每组输出占据一行,为一个整数,表示最大的价值之和。

样例输入:

3 4

0 5 2 3

4 5 6 7

8 9 10 11

样例输出:

42 

#include<iostream>
using namespace std;
int a[1001][1001], b[1001][1001];
int fun(int a[][1001], int b[][1001], int m, int n, int x, int y){
	//以空间换时间 
	if(b[x][y]>0) return b[x][y];
	//移动至出口 
	if(x==m-1&&y==n-1) return 0;
	//若移至最后一行,则只能向右走 
	if(x==m-1) return b[x][y]=fun(a, b, m, n, x, y+1)+a[x][y+1];
	//若移至最后一列,则只能向下走 
	if(y==n-1) return b[x][y]=fun(a, b, m, n, x+1, y)+a[x+1][y];
	//如果可以向下或向右走,则判断:
	//1、向下走(进入下一层递归,并加上本次向下走得到的宝藏价值)
    //2、向右走(进入下一层递归,并加上本次向右走得到的宝藏价值) 
	//1与2递归的结果取最大值 
	return b[x][y]=max(fun(a, b, m, n, x+1, y)+a[x+1][y], fun(a, b, m, n, x, y+1)+a[x][y+1]);
}
int main()
{
	int m, n;
	while(cin>>m>>n){
		for(int i=0;i<m;i++)
			for(int j=0;j<n;j++){
				cin>>a[i][j];
				b[i][j]=-1;//给b赋值-1,便于递归函数中的判断 
			}		
		cout<<fun(a, b, m, n, 0, 0)<<endl;
	}
	return 0;
}

两题求的分别是所放箱子和获得宝藏的最大价值,且分为多步过程。

由于采用动态规划算法思想(将求大问题最优解,分解为求子问题的最优解),所以需要问题分解。

既然涉及到问题的分解,且子问题与原问题求解过程一致,改变的又仅仅是数据量。因此自然想到递归,但递归最大的缺陷是耗时。

耗时的原因是:在各个子问题中,存在相同的求解过程,有大量的重复计算,累计下来的时间是惊人的,所以需要避免重复。那么如果在第一次的求解中就将结果记录下来,当下次遇到重复的问题时,能直接取用已经记录的结果,以避免重复计算,也就是“以空间换时间”。

所以基本思想:递归+数据记录

在dp的简单应用中,问题一般分解为两种,如题一中的“放”与“不放”,题二中的“向下”与“向右”,而判断的,就是在某一步中哪个分解方式更优。

需要采用层层递归深入,最后回归每一步中的最优解,得到大问题的最优解,再结合记录的数据缩减运行时间。

具体数据记录方式:在递归返回结果时,一并存下数据。并将已经记录的数据值作为“递归出口”之一,当再次使用,就无需递归,直接返回记录即可。

(题一是小规模的简单0-1背包问题,因此本题中只用了递归;而题二无论规模,还是重复计算量,都避免不了数据记录)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值