寒假训练 第十七节 动态规划 总结

文章讲述了如何利用动态规划算法解决一系列最优化问题,包括宠物小精灵的收服策略,摘花生的最大收益,以及方格图中寻找最大数字和的路径。这些问题都涉及到在有限资源约束下做出最优决策,并给出了相应的状态转移方程和代码实现。
摘要由CSDN通过智能技术生成

U280641 宠物小精灵之收服
输入格式
输入数据的第一行包含三个整数:N,M,K,分别代表小智的精灵球数量、皮卡丘初始的体力值、野生小精灵的数量。

之后的K行,每一行代表一个野生小精灵,包括两个整数:收服该小精灵需要的精灵球的数量,以及收服过程中对皮卡丘造成的伤害。

输出格式
输出为一行,包含两个整数:C,R,分别表示最多收服C个小精灵,以及收服C个小精灵时皮卡丘的剩余体力值最多为R。

f[i, j1, j2]
			1. 集合 : 从 前 i 件物品中选, 一维体积不超过 j1 (精灵球)
					二维体积不超过 j2 (体力),的所有选法的集合
					
					w[i] = 1, 小精灵的价值看成 1
					
					max(价值) 
					
			2. 计算集合
					f[i, j1, j2]    v1[i], v2[i] 
					
					第 i 件物品 选 或 不选 
					f[i, j1, j2] = max(f[i - 1, j1, j2], f[i - 1, j1 - v1i, j2 - v2i] + 1);
					
			3. 边界
					f[0, j1, j2] = 0
					
			4. 时间: O(n^3)
#include <iostream>
using namespace std;
const int N = 1010, M = 510, K = 110;
int n, m, k;
int f[N][M];
int v1[K], v2[K];
int main()
{
	cin >> n >> m >> k;
	for (int i = 1; i <= k; i ++ ) cin >> v1[i] >> v2[i];
	for (int i = 1; i <= k; i ++)  // 物品数
		for (int j1 = n; j1 >= v1[i]; j1 -- )  // 一维体积(精灵球)
			for (int j2 = m; j2 >= v2[i]; j2 -- )
				f[j1][j2] = max(f[j1][j2], f[j1 - v1[i]][j2 - v2[i]] + 1);
	int res = -1, t;  // res 代表max(价值), t 消耗的二维体积(体力)
	for (int i = 0; i <= m - 1; i ++ )
		if (res < f[n][i])
		{
			res = f[n][i];
			t = i;
		}
	 cout << res << ' ' << m - t << endl;
	 return 0;
	
}

U280643 摘花生
Hello Kitty想摘点花生送给她喜欢的米老鼠。
她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。
地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的花生。
Hello Kitty只能向东或向南走,不能向西或向北走。
问Hello Kitty最多能够摘到多少颗花生。
在这里插入图片描述
每个点无非就是从它上面或者左边走过来,选择较大的一个就好。状态转移方程:f[i][j]=max(f[i-1][j],f[i][j-1])i-1,j即为从上面过来;i,j-1即为从左边过来。最后输出出口处即可。

最后输出一定要用printf才能ac,使用cout将无法ac。
cout和printf的区别

#include<iostream>
#include<algorithm>
using namespace std;
int dp[105][105],f[105][105];
int c,r,maxn;
int main()
{
	int s;
	cin>>s;
	for(int i=0;i<s;i++)
	{
		cin>>c>>r;
		for(int i=1;i<=c;i++)
			for(int j=1;j<=r;j++)
			{
				cin>>dp[i][j];
				f[i][j]=dp[i][j];
			}
		for(int i=1;i<=c;i++)
			for(int j=1;j<=r;j++)
				f[i][j]+=max(f[i-1][j],f[i][j-1]);
		printf("%d\n",f[c][r]);
	}
}

U280638 方格取数
设有 N×N 的方格图,我们在其中的某些方格中填入正整数,而其它的方格中则放入数字0。如下图所示:
在这里插入图片描述
某人从图中的左上角 A 出发,可以向下行走,也可以向右行走,直到到达右下角的 B 点。
在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。
此人从 A 点到 B 点共走了两次,试找出两条这样的路径,使得取得的数字和为最大。
输入格式
第一行为一个整数N,表示 N×N 的方格图。
接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。
行和列编号从 1 开始。
一行“0 0 0”表示结束。
输出格式
输出一个整数,表示两条路径上取得的最大的和。

我们分两次从(1,1)(n,n)分别求局部最优解,
但是这样不可取,因为我们第一次走完之后,产生的结果会对
第二次的取值产生影响,所以不能分开求。
那么我们就两次取数同时开始,定义数组dp[N][N][N],
dp[k][i1][i2]表示第一次的路径从(1,1)走到(i1,k-i1)以及第二次的路径从(1,1)走到(i2,k-i2)
所能取到的最大的数字和。
其中dp[k][i1][i2]表示:k=i1+i2,在k==n+n的时候就是到达了终点,
i1表示第一次取数走到了第i1行,根据k==i1+j1,j1=k-i1,说明第一次取数走到了(i1,j1)==>(i1,k-i1),
同理,i2表示第二次取数的路径到达了(i2,j2)==>(i2,k-i2)

在这里插入图片描述
一共有四种状态可以到大dp[k][i1][i2],所以我们在这四种情况中取max。
当i1=i2时,(i1,j1)==(i2,j2),而题目说当某次取走某个点的数之后,这个点就变成了0,所以当我们的路径重叠时,我们只能加一次这个点所以我们在循环里面要if判断。

#include<iostream>
using namespace std;
const int N=15;
int n;
int map[N][N];
int dp[2*N][N][N];
 
void qv(){
    for(int k=2;k<=2*n;k++){
        for(int i1=1;i1<=n;i1++){
            for(int i2=1;i2<=n;i2++){
                int j1=k-i1,j2=k-i2;
                if(1<=i1&&i1<=n&&1<=i2&&i2<=n){
                    int &x=dp[k][i1][i2];
                    int t=map[i1][j1];
                    if(i1!=i2)t+=map[i2][j2];
                    x=max(x,dp[k-1][i1-1][i2-1]);
                    x=max(x,dp[k-1][i1-1][i2]);
                    x=max(x,dp[k-1][i1][i2-1]);
                    x=max(x,dp[k-1][i1][i2]);
                    x+=t;
                }
            }
        }
    }
    cout<<dp[2*n][n][n]<<endl;
}
 
int main()
{
    cin>>n;
    int a,b,c;
    while(cin>>a>>b>>c,a||b||c)map[a][b]=c;
    qv();
    return 0;  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值