【Luogu】动态规划四
P1095 [NOIP 2007 普及组] 守望者的逃离 - 洛谷
思路:
其实是贪心的dp
一个简单想法是我们可以可以定义dp[i] 为 i 秒内能走过的最大距离,但是由于有魔法限制,我们发现简单的一维是不够的,我们应该还需要一个维度来维护当前魔法
当然我们也可以换一个想法,我们可以直接模拟,用两个人来代表用魔法跑路和走路
如果魔法跑的更快,那么我们最优的选法肯定是选魔法,否则那就走路
模拟过程中我们可以持续更替最大值,同时判断是否能跑出去
至于为什么可以这样,其实也很显然,如果存在一个时间点有 魔法距离 > 光跑步距离,那么此时我们为何不回溯到一开始然后使用魔法策略跑,这显然是更优的
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include <iomanip>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <utility>
#include <array>
#include <tuple>
using namespace std;
#define int long long
#define yes cout << "Yes" << endl
#define no cout << "No" << endlvoid solve()
{int M, S, T;cin >> M >> S >> T;int lenmagic = 0, lenrun = 0;for (int i = 1; i <= T; i++){if (M >= 10){M -= 10;lenmagic += 60;}else{M += 4;lenrun = max(lenrun, lenmagic);}lenrun += 17;if (max(lenmagic,lenrun) >= S){yes;cout << i;return;}}no;cout << max(lenmagic, lenrun);
}
signed main()
{//cin.tie(0)->sync_with_stdio(false);int t = 1;//cin >> t;while (t--){solve();}return 0;
}
P1077 [NOIP 2012 普及组] 摆花 - 洛谷
思路:
题目让我们分配花,最后一定要有m朵
我们定义dp[i][j] 为前 i 种花分配了 j 朵的方法数
那么对于前 i 种花,我们可以枚举 j 的所有可能,同时再对当前的第 i 种枚举这种花选几朵
然后三重循环枚举即可
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include <iomanip>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <utility>
#include <array>
#include <tuple>
using namespace std;
#define int long long
#define yes cout << "YES" << endl
#define no cout << "NO" << endl
const int MOD = 1e6 + 7;
int f[105][105];
void solve()
{int n, m;cin >> n >> m;vector<int> a(n+1);for (int i = 1; i <= n; i++){cin >> a[i];}f[0][0] = 1;//枚举前 i 朵花for (int i = 1; i <= n; i++){//枚举选 0 ~ m 种的所有可能for (int j = 0; j <= m; j++){//第 i 种花选 k 朵for (int k = 0; k <= min(j,a[i]); k++){f[i][j] += f[i - 1][j - k];f[i][j] %= MOD;}}}cout << f[n][m];
}
signed main()
{//cin.tie(0)->sync_with_stdio(false);int t = 1;//cin >> t;while (t--){solve();}return 0;
}
P2066 机器分配 - 洛谷
思路:
划分dp
我们可以定义 dp[i][j] 为前 i 个公司选了 j 台的最大利润
然后和上题一样三重循环枚举,特别的,由于要字典序最小,所以我们可以倒着枚举
同时储存我们可以定义一个 path[i][j][h] 其代表 对于 前 i 个公司 选了 j 台的方案中 第 h 家公司选了几台
具体转移看代码
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include <iomanip>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <utility>
#include <array>
#include <tuple>
using namespace std;
#define int long long
#define yes cout << "YES" << endl
#define no cout << "NO" << endl
int path[11][16][11];
void solve()
{int n, m;cin >> n >> m;vector<vector<int>> mp(n + 1, vector<int>(m + 1, 0));for (int i = 1; i <= n; i++){for (int j = 1; j <= m; j++){cin >> mp[i][j];}}vector<vector<int>> f(n + 1, vector<int>(m + 1, 0));for (int i = 1; i <= n; i++){//一共选 j 台for (int j = 0; j <= m; j++){//这层选 k 台for (int k = j; k >= 0; k--){if (f[i][j] < f[i - 1][j - k] + mp[i][k]){f[i][j] = f[i - 1][j - k] + mp[i][k];//i j 方案中 第 h 个公司应该分配多少个for (int h = 1; h < i; h++) path[i][j][h] = path[i - 1][j - k][h];path[i][j][i] = k;}}}}cout << f[n][m] << endl;for (int i = 1; i <= n; i++) cout << i << " " << path[n][m][i] << endl;
}
signed main()
{//cin.tie(0)->sync_with_stdio(false);int t = 1;//cin >> t;while (t--){solve();}return 0;
}
P2758 编辑距离 - 洛谷
思路:
比较经典的一题
我们可以定义 dp[i][j] 为将 a 的前 i 个字符与 b 的前 j 个字符匹配好需要的最小操作数
那么初始化比较特别,对于所有的 dp[0][j] 有 dp[0][j] = j,同理 dp[i][0] = i,因为是有一个是空的,所以肯定是其长度(全删掉或者全加上)
接下来看转移,显然对于一次转移我们最多只有一次操作,且有以下三种情况
①.从 dp[i-1][j-1] 转移来,此时前 i-1 和 j-1 个字符已经处理完毕,那么只需要在a[i-1]后增加上b[i]即可,特别的,如果 a[i] = b[j],那么就不需要操作
②.从 dp[i-1][j] 转移来,此时 i - 1 与 j 已经匹配完毕,说明 a[i] 是多余的,那么就要执行删除操作
③.从 dp[i][j-1] 转移来,此时 i 与 j - 1 相互匹配,说明 b[j] 还没匹配,那么增加 b[j] 即可
然后最后就是三者取最小值即可
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include <iomanip>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <utility>
#include <array>
#include <tuple>
using namespace std;
#define int long long
#define yes cout << "YES" << endl
#define no cout << "NO" << endlvoid solve()
{string a, b;cin >> a >> b;int lena = a.length(), lenb = b.length();vector<vector<int>> f(lena + 1, vector<int>(lenb + 1));for (int i = 1; i <= lena; i++){f[i][0] = i;}for (int i = 1; i <= lenb; i++){f[0][i] = i;}a = "#" + a;b = "#" + b;for (int i = 1; i <= lena; i++){for (int j = 1; j <= lenb; j++){if (a[i] == b[j]){f[i][j] = f[i - 1][j - 1];}else{f[i][j] = min(f[i-1][j-1], min(f[i-1][j],f[i][j-1])) + 1;}}}cout << f[lena][lenb];
}
signed main()
{//cin.tie(0)->sync_with_stdio(false);int t = 1;//cin >> t;while (t--){solve();}return 0;
}