动态规划杂题(一)

1、奶牛零食

https://www.acwing.com/problem/content/4561/
题意:给我们一个序列,我们可以从前面拿一个数,也可以从后面拿一个数,然后每拿一个数都会有一个值,这个值是i ∗ a [ i ] i*a[i]ia[i],然后问我们最大值是多少。

思路 :首先我们进行状态表示f[i][j表示的是我们在前面取i个数和在后面取j个数的权值最大是多少。属性就是Max。然后我们把集合划分为两个部分,一个是最后一次弹出的是i,另一个则是最后一个弹出的是j。那么状态转移方程就是f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] + ( i + j ) ∗ a [ i ] , f [ i ] [ j − 1 ] + ( i + j ) ∗ b [ j ] ) f[i][j]=max(f[i-1][j]+(i+j)*a[i],f[i][j-1]+(i+j)*b[j])f[i][j]=max(f[i1][j]+(i+j)a[i],f[i][j1]+(i+j)b[j])

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 2010;
int a[N];
int b[N];
long long f[N][N];

int main()
{
     ios::sync_with_stdio(false);
     cin.tie(0);
     cout.tie(0);
     int n;
     cin >> n;
     for (int i = 1; i <= n; i++)
     {
          cin >> a[i];        //我们读入a[i]
          b[n - i + 1] = a[i];//我们倒着进行b[]数组的额记录
     }

     for (int i = 1; i <= n; i++)//首先我们要考虑特殊的情况,这个按照我们集合的表示就是一直从前面选
          f[i][0] = f[i - 1][0] + i * a[i];
     for (int i = 1; i <= n; i++)//这个就是我们一直从后面选
          f[0][i] = f[0][i - 1] + i * b[i];

     for (int i = 1; i <= n; i++)//由于我们在上面已经判断过0的情况了,所以我们就直接从1开始
     {
          for (int j = 1; j <= n - i; j++)//i+j的和不能超过n
          {
               //我们正常的转态转移方程
               f[i][j] = max(f[i - 1][j] + (i + j) * a[i], f[i][j - 1] + (i + j) * b[j]);
          }
     }
     long long res = 0;
     for (int i = 0; i <= n; i++)
     {
          res = max(res, f[i][n - i]);
     }
     cout << res << endl;
     return 0;
}

2、免费陷阱

题意:现在给我们一个从0到10的数轴,然后每一秒钟都会有一些馅饼掉在这个数轴上,在每一秒钟一个点只能掉下来一个馅饼。然后我们一开始在5的位置,我们一次可以接相邻两个点的馅饼,然后我们每秒钟可以向左或者向右移动一个单位。问我们最多可以接到多少个馅饼。
思路:其实这个题比较不好看,但是同过分析我们可以看到它其实是一个数字三角形的变形,只不过我们这里以相邻的三个点作为一个点类似这样的一个图
在这里插入图片描述
然后我们就可以进行状态表示,f[i][j]表示的是我们从第i秒到走到下标是j的地方我们可以拿到到少馅饼,属性就是Max。状态的计算就是f [ i ] [ i ] = m a x ( f [ i + 1 ] [ j ] , m a x ( f [ i + 1 ] [ j − 1 ] , f [ i + 1 ] [ j + 1 ] ) ) f[i][i]=max(f[i+1][j],max(f[i+1][j-1],f[i+1][j+1]))f[i][i]=max(f[i+1][j],max(f[i+1][j1],f[i+1][j+1]))
然后我们就可以写出代码

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 100001;
int f[N][20];

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n;
	while (cin >> n)
	{
		if (n == 0)
			break;
		int t = -1;
		memset(f, 0, sizeof f);
		for (int i = 0; i < n; i++)
		{
			int x, y;
			cin >> x >> y;
			f[y][x]++;//我们一边读入一边记录
			if (y > t)
				t = y;//看我们最长的时间是多少
		}
		for (int i = t; i >= 0; i--)//从我们的第t秒倒着找。
		{
			for (int j = 10; j >= 0; j--)
			{
				f[i][j] = max(f[i + 1][j], max(f[i + 1][j - 1], f[i + 1][j + 1])) + f[i][j];
			}
		}
		cout << f[0][5] << endl;
	}
	return 0;
}

3、猪猪存钱罐

思路:首先这个题我们通过分析的话,可以得出我们其实要求的是一个,完全背包的问题,但是我们完全背包的状态表示要变一下。此时的f[i][j]表示的是我们从前i个物品中选,然后重量恰好等于j的最小价值是多少,然后它的属性就是Min。下面我们来看状态计算,我们可以按照完全背包一样的划分就可以。

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1e6 + 10;
int v[N];
int w[N];
int f[N];

void solve()
{
	int E,F;
	cin>>E>>F;
	int V = F - E;
	int n;
	cin>>n;
	memset(f,0x3f,sizeof f);
	f[0]=0;
	for(int i=1;i<=n;i++)
	{
		cin>>w[i]>>v[i];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=v[i];j<=V;j++)
		{
			f[j]=min(f[j],f[j-v[i]]+w[i]);	
		}
	}
	if(f[V]==0x3f3f3f3f)
		cout<<"This is impossible."<<'\n';
	else
		cout<<"The minimum amount of money in the piggy-bank is "<<f[V]<<'.'<<'\n';
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int T;
	cin>>T;
	while(T--)
	{
		solve();
	}
	return 0;
}

版权声明:本文为yuanxinya_原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。