GDUT22级寒假训练专题二

文章介绍了两个使用动态规划方法解决的数学问题:一是寻找二维数组中最长的滑坡路径,二是01背包问题的变种,要求最大化智商与情商之和。在滑坡问题中,使用了记忆化搜索优化,而在好友好感度问题中,通过dp数组找出最优分配策略以最大化好感度总和。
摘要由CSDN通过智能技术生成

题解

B - 滑雪

传送门

题意

    Michael 想知道在一个区域中最长的滑坡。区域由一个行数为 R R R和列数为 C C C二维数组给出。数组的每个数字代表点的高度。下面是一个例子:

1 1 1 2 2 2 3 3 3 4 4 4 5 5 5
16 16 16 17 17 17 18 18 18 19 19 19 6 6 6
15 15 15 24 24 24 25 25 25 20 20 20 7 7 7
14 14 14 23 23 23 22 22 22 21 21 21 8 8 8
13 13 13 12 12 12 11 11 11 10 10 10 9 9 9

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度会减小。在上面的例子中,一条可行的滑坡为 24 − 17 − 16 − 1 24-17-16-1 2417161(从 24 24 24开始,在 1 1 1结束)。当然 25 - 24 - 23 - … - 3 - 2 - 1 25-24-23-\ldots-3-2-1 252423321更长。事实上,这是最长的一条。
1 ≤ R , C ≤ 100 1≤R,C≤100 1R,C100

思路

    对于二维数组中的每一点,将从该点出发的最长滑坡长度记录到 d p dp dp数组中。则:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] , d p [ i + 1 ] [ j ] , d p [ i ] [ j + 1 ] ) + 1 dp[i][j] = max(dp[i-1][j],dp[i][j-1],dp[i+1][j],dp[i][j+1])+1 dp[i][j]=max(dp[i1][j],dp[i][j1],dp[i+1][j],dp[i][j+1])+1
用记忆化搜索的方式实现dp。初始化 d p dp dp数组为 0 0 0,搜索到某一点,四个方向都无法滑,返回1。

代码

#include <bits/stdc++.h>
using namespace std;

int h, l;
int Tmap[105][105];
int dp[105][105] = { 0 };
int book[105][105] = { 0 };
const int dx[4] = { 0,0,1,-1 };
const int dy[4] = { 1,-1,0,0 };
int Tans = 0,ans = 0;
int dfs(int x, int y)
{
	if (dp[x][y] != 0)
		return dp[x][y];
	for (int i = 0; i < 4; i++)
	{
		int nx = x + dx[i], ny = y + dy[i];
		if (nx < 1 || nx > h || ny < 1 || ny > l)
			continue;
		if (Tmap[nx][ny] < Tmap[x][y] && book[nx][ny] == 0)
		{
			book[x][y] = 1;
			dp[x][y] = max(dfs(nx, ny) + 1,dp[x][y]);
			book[x][y] = 0;
		}
	}
	if (dp[x][y] == 0)
		dp[x][y] = 1;
	return dp[x][y];
}

int main()
{
	cin >> h >> l;
	for (int i = 1; i <= h; i++)
		for (int j = 1; j <= l; j++)
			cin >> Tmap[i][j];
	for (int i = 1; i <= h; i++)
		for (int j = 1; j <= l; j++)
			Tans = max(Tans, dfs(i, j));
	cout << Tans;
	return 0;
}

I - Cow Exhibition G

传送门

题意

    奶牛想证明它们是聪明而风趣的。为此,贝西筹备了一个奶牛博览会,她已经对 N N N头奶牛进行了面试,确定了每头奶牛的智商 S i S_i Si和情商 F i F_i Fi
    贝西有权选择让哪些奶牛参加展览(可以不让任何奶牛参加展览,如果这样做是最好的,输出 0 0 0。)。由于负的智商或情商会造成负面效果,所以贝西不希望出展奶牛的智商之和小于零,或情商之和小于零。满足这两个条件下,她希望出展奶牛的智商与情商之和越大越好,请帮助贝西求出这个最大值。
1 ≤ N ≤ 400 , − 1000 ≤ S i , F i ≤ 1000 1≤N≤400,-1000≤S_i,F_i≤1000 1N4001000Si,Fi1000

思路

    对于任意一头奶牛都有选和不选两种情况,联想到 01 01 01背包中物品的放与不放。将奶牛的智商之和的背包容量,每头奶牛的智商当作物品体积,情商当作物品价值。
    则 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i头奶牛,智商之和为 j j j的情况下情商之和的最大值。滚动数组优化后得到:
d p [ j ] = m a x ( d p [ j ] , d p [ j − S [ i ] ] + F [ i ] ) dp[j] = max(dp[j], dp[j - S[i]] + F[i]) dp[j]=max(dp[j],dp[jS[i]]+F[i])
    但是题目中的 S i , F i S_i,F_i Si,Fi可能为负数,将dp数组扩大一倍开到 800005 800005 800005,将 d p [ 400000 ] dp[400000] dp[400000]当作智商之和为0的情况。
    正常 01 01 01背包滚动数组倒着循环即可,但是此题中 S i S_i Si 有正有负,无法判断是正着循环还是倒着循环,于是可以先判断正负再选择正着循环还是倒着循环。

代码

#include <bits/stdc++.h>
using namespace std;

int dp[800005] = { 0 };//使排列组合后能出现的iq值之和的dp[iq]为eq
int main()
{
	memset(dp, ~0x3f3f3f3f, sizeof(dp));//初始化dp数组为计算后也不会溢出的足够大的负数,使排列组合后不能出现的iq值对应eq为负数
	dp[400000] = 0;
	int n;
	int iq[405],eq[405];
	cin >> n;
	int ans = 0;
	for (int i = 0; i < n; i++)
		cin >> iq[i] >> eq[i];
	for (int i = 0; i < n; i++)
	{
		if (iq[i] >= 0)
			for (int j = 800000; j >= iq[i]; j--)
				dp[j] = max(dp[j], dp[j - iq[i]] + eq[i]);
		else
			for (int j = 0; j <= 800000 + iq[i]; j++)
				dp[j] = max(dp[j], dp[j - iq[i]] + eq[i]);
	}
	for (int i = 400000; i <= 800000; i++)
	{
		if (dp[i] >= 0)
			ans = max(ans, i + dp[i] - 400000);
	}
	cout << ans;
	return 0;
}

M-本题主要考察了找规律

传送门

题意

   小波奇由于冲动消费,不小心买多了章鱼仙贝,买了一共 m m m份章鱼仙贝,于是她只能把这些仙贝分给 n n n位朋友。
   若小波奇当前还剩下 x ( x > 0 ) x(x>0) x(x>0)个仙贝,并给了一位朋友 y y y个仙贝( x , y x,y x,y都为整数),则这位朋友对小波奇的好感度将增加 y / x y/x y/x(这个值可以为小数)。
   现在,小波奇可以任意安排送仙贝的顺序和每次送仙贝的个数,但不能给同一个人送两次仙贝,允许最后手中还有剩余的仙贝,允许最终有朋友没有分到仙贝。社恐的朋友非常重要,所以请你帮助小波奇算一算,在最优送仙贝策略下,小波奇和所有人的好感度之和最大为多少(假设初始小波奇和所有人好感度都为 0 0 0)。
( 1 ≤ n , m ≤ 500 ) (1≤n,m≤500) (1n,m500)

思路

   牛客训练时碰到的感兴趣的 d p dp dp题。被题目坑了,完全没规律
   感觉难点主要是看不出来用 d p dp dp来做,建立一个 d p dp dp数组, d p [ i ] [ j ] dp[i][j] dp[i][j]表示已经给 i i i个人发了仙贝,一共发了 j j j个仙贝时获得好感度之和的最大值。则:
d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − 1 ] [ j − k ] + k / ( m − j + k ) ) , 0 ≤ k ≤ j dp[i][j] = max(dp[i][j],dp[i - 1][j - k] + k/(m - j + k)),0≤k≤j dp[i][j]=max(dp[i][j],dp[i1][jk]+k/(mj+k))0kj

代码

#include <bits/stdc++.h>
using namespace std;

double dp[505][505] = { 0 };
int main()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1;i <=n;i++)
        for(int j = 1;j <=m;j++)
            for(int k = 0;k <= j;k++)
                dp[i][j] = max(dp[i][j],dp[i - 1][j - k] + (double)k/(m - j + k));
    printf("%.9lf",dp[n][m]);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值