线性dp

A

题目链接

核心思路:其实这压根不是一个正儿八经的dp,就是一个简单的模拟题。所以不需要想复杂了。我们就开两个变量s1和s1来存储一个闪现走的步数和步行走的步数。然后按照题目模拟与比较就好了。如果闪现比走路要快那么就把那个值赋给它就好了.

#include<iostream>
#include<algorithm>
#include<bits/stdc++.h>
using namespace std;
typedef  unsigned long long LL;
const int N = 1e2 + 10;
int a[N][N], f[N][N];
int n, m;
int main()
{
	int m, s, t;
	cin >> m >> s >> t;
	int s1 = 0, s2 = 0;
	for (int i = 1;i <= t;i++)
	{
		s1 += 17;
		if (m >= 10)
		{
			s2 += 60;
			m -= 10;
		}
		else
			m += 4;
		if (s2 > s1)
			s1 = s2;
		if (s1 > s)
		{
			cout << "Yes" << endl << i << endl;
			return 0;
		}
	}
	cout << "No" << endl << s1 << endl;
}

B

题目链接

核心思路:就是一个背包问题。

集合定义:\(f[i][j]表示的是从前i个多花里面选,然后栽种j盆的方案数\)

集合划分:

  • 对第i种花选1盆
  • 对第i种花选2盆
  • ......
  • 对第i种花选a[i]盆

状态转移方程:\(f[i][j]+=f[i-1][j-k]\)

空间优化也是老样子,倒着枚举体积也就是j就好了.

#include<bits/stdc++.h>
using namespace std;
const int maxn=105, mod = 1000007;
int n, m, a[maxn], f[maxn];
int main()
{
    cin>>n>>m;
    for(int i=1; i<=n; i++) cin>>a[i];
    f[0] = 1;
    for(int i=1; i<=n; i++)
        for(int j=m; j>=0; j--) //注意,是01背包
            for(int k=0; k<=min(a[i], j); k++)
              f[j] = (f[j] + f[j-k])%mod;
    cout<<f[m]<<endl;
    return 0;
}

C

核心思路:说是简单dp但是也还不算很好做,可能是我太菜了.

集合定义:\(f[i][0]表示的是走到第i行的线段的左端点的最少步数,f[i][1]则表示的是右端点的一个最少步数\)

集合划分:

  • 由上面一行的左端点,走到\(f[i][0]\)也就是i行线段的左端点。一定要注意的是我们的一个前提是需要走完这条线段
  • 由上一行的右端点,走到\(f[i][0]\).
  • 由上一行的左端点,走到\(f[i][1]\)
  • 由上一行的右端点,走到\(f[i][0]\)
#include<bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
int a[N][2], f[N][2];
int n;
int dis(int a, int b)//计算距离
{
	return abs(a - b);
}
int main()
{
	cin >> n;
	for (int i = 1;i <= n;i++)
	{
		cin >> a[i][0] >> a[i][1];
	}
	f[1][1] = dis(a[1][1], 1);
	f[1][0] = dis(a[1][1], 1) + dis(a[1][1], a[1][0]);
	for (int i = 2;i <= n;i++)
	{
		f[i][0] = min(f[i - 1][0] + dis(a[i - 1][0], a[i][1]) + dis(a[i][1], a[i][0]), f[i - 1][1] + dis(a[i - 1][1], a[i][1]) + dis(a[i][1], a[i][0])) + 1;
		f[i][1] = min(f[i - 1][0] + dis(a[i - 1][0], a[i][0]) + dis(a[i][0], a[i][1]), f[i - 1][1] + dis(a[i - 1][1], a[i][0]) + dis(a[i][0], a[i][1])) + 1;
	}
	cout << min(f[n][0] + dis(a[n][0], n), f[n][1] + dis(a[n][1], n));
}

D

题目链接

集合定义:\(f[a][b][c][d]\)表示的是第一类卡放了a张,第二类卡放了b张,第三类卡放了c张,第四类卡放了d张的一个方案数.

集合划分:其实我们每一维可以单独看成 一个背包问题,所以也就变成了选和不选的问题.

  • 不选这张牌,那我们的体积不变
  • 选了这张牌,那我们减去对应的体积加上对应的价值.

状态转移方程:我们对每一维都判断下就好了.

下面讲解下r为什么是这样表示的,其实这个是很简单的。因为题目要求我们选对应的数字卡牌就跳多少步,之所以从1开始的原因是因为我们刚开始就在起点.

#include<bits/stdc++.h>
using namespace std;
const int N =41;
int f[N][N][N][N], num[400], g[5], n, m, x;
int  main()
{
	cin >> n >> m;
	for (int i = 1;i <= n;i++)
		cin >> num[i];
	f[0][0][0][0] = num[1];
	for (int i = 1;i <= m;i++)
	{
		int x;
		cin >> x;
		g[x]++;
	}
	for(int a=0;a<=g[1];a++)
		for(int b=0;b<=g[2];b++)
			for(int c=0;c<=g[3];c++)
				for (int d = 0;d <= g[4];d++)
				{
					int r = 1 + a + 2 * b + 3 * c + 4 * d;//这是题目给的条件,一定要仔细看题目
					if (a > 0)
						f[a][b][c][d] = max(f[a][b][c][d], f[a - 1][b][c][d] + num[r]);
					if (b > 0)
						f[a][b][c][d] = max(f[a][b][c][d], f[a][b - 1][c][d] + num[r]);
					if (c > 0)
						f[a][b][c][d] = max(f[a][b][c][d], f[a][b][c - 1][d] + num[r]);
					if (d > 0)
						f[a][b][c][d] = max(f[a][b][c][d], f[a][b][c][d - 1] + num[r]);
				}
	cout << f[g[1]][g[2]][g[3]][g[4]];
	return 0;
}

小结

线性dp初始化的时候一定要注意需要在我们dp过程中初始化,就像是搜索一样,一边搜索一边初始化。

posted @ 2022-12-28 12:13  努力的德华  阅读(26)  评论(0)    收藏  举报