线性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过程中初始化,就像是搜索一样,一边搜索一边初始化。

浙公网安备 33010602011771号