DP Ⅰ
远古题可能题意有点抽象,大致是有向图找最长路,这样我们就想到一个类似树形dp的dfs写法,每个未遍历的节点向出点dfs,记录每个点的关键孩子的编号,这里的关键孩子指的是地雷数量最多的孩子,一直更新即可。最后dfs寻找答案。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 30;
int n, a[N], son[N], res[N], k, ans, vis[N];
vector <int> e[N];
void dfs(int id)
{
if(vis[id]) return;
vis[id] = 1;
for(int it : e[id])
{
dfs(it);
if(res[it] > res[son[id]])
{
res[id] -= res[son[id]];
res[id] += res[it];
son[id] = it;
}
}
res[id] += a[id];
}
void out(int id)
{
if(id == 0) return;
cout << id << ' ';
out(son[id]);
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
int x;
for(int i = 1; i < n; i ++)
{
for(int j = i + 1; j <= n; j ++)
{
cin >> x;
if(x) e[i].push_back(j);
}
}
for(int i = 1; i <= n; i ++)
{
if(!vis[i]) dfs(i);
if(res[i] > ans)
{
ans = res[i];
k = i;
}
}
out(k);
cout << endl << ans << endl;
return 0;
}
非常典型的拓扑排序,但是注意求的是总数,而不是长度
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5010, mod = 80112002;
int n, m, deg[N], res[N], ans;
vector <int> e[N];
int main()
{
cin >> n >> m;
int u, v;
for(int i = 1; i <= m; i ++)
{
scanf("%d %d", &u, &v);
e[u].push_back(v);
deg[v] ++;
}
queue <int> q;
for(int i = 1; i <= n; i ++)
{
if(!deg[i]) q.push(i), res[i] = 1;
}
while(!q.empty())
{
int id = q.front();
q.pop();
for(int it : e[id])
{
res[it] += res[id];
res[it] %= mod;
if(!-- deg[it]) q.push(it);
}
}
for(int i = 1; i <= n; i ++)
{
if(e[i].size() == 0) ans += res[i], ans %= mod;
}
cout << ans << "\n";
return 0;
}
不错的思维题。首先注意到胜利和失败都会有一定的价值,并且需要一定代价,那么可以转换成01背包:胜利获得经验-失败获得经验是价值,需要药的量就是代价。至于为什么可以将价值这么描述,以下是非严谨说明:
首先,无论如何,全选肯定是最优的,因为即使你剩余0的药量,仍然可以失败获得经验,所以可以先给最终结果ans加上每个人失败获得经验的总和,那么接下来如果指定某个人获胜,那么ans将会减去失败获得经验,再加上胜利获得经验,也就是加上(胜利-失败),那么就完成了转换。
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e3 + 10, M = 1e6 + 10;
int n, m, a[N], b[N], c[N], w[N], ans, sum, f[N];
signed main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++)
{
scanf("%lld %lld %lld", &a[i], &b[i], &c[i]);
ans += a[i];
w[i] = b[i] - a[i];
}
for(int i = 1; i <= n; i ++)
{
for(int j = m; j >= c[i]; j --)
{
f[j] = max(f[j], f[j - c[i]] + w[i]);
}
}
ans = (ans + f[m]) * 5;
cout << ans << endl;
return 0;
}
这道题让我们求最短路使得所有线段被覆盖。
自然想到用dp[i][j]表示到第i行的线段,状态为j的最短覆盖路长度,其中当j为0,表示第i条线段最后被经过的点是左端点,当j为1时,表示第i条线段最后被经过的点是右端点。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e4 + 10, inf = 500000000;
int n, l[N], r[N], f[N][5];
int solve(int id, int x, int y) { return abs(id - x) + abs(y - x); }
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
scanf("%d %d", &l[i], &r[i]);
f[i][0] = f[i][1] = inf;
}
f[1][0] = r[1] - 1 + r[1] - l[1], f[1][1] = r[1] - 1;
for(int i = 2; i <= n; i ++)
{
f[i][0] = min(f[i - 1][0] + solve(l[i - 1], r[i], l[i]), f[i - 1][1] + solve(r[i - 1], r[i], l[i])) + 1;
f[i][1] = min(f[i - 1][0] + solve(l[i - 1], l[i], r[i]), f[i - 1][1] + solve(r[i - 1], l[i], r[i])) + 1;
}
cout << min(f[n][0] + n - l[n], f[n][1] + n - r[n]);
return 0;
}
这是一个具有依赖性质的背包。
代价就是物品价格,价值是重要度乘价格。
如果添加一个题目条件:买主件必须买附件,那么通过合并就可以转换成一个朴素的01背包,但是这题显然不能这么考虑。
其实也很简单。
传统的01背包只有两种情况:
选它,或不选它。
这道题只需要多考虑几种:
1.不选它。
2.选它。
3.选它和其中一个附件。
4.选它和其中另一个附件。
5.选它和其中两个附件。
笔者在做题时,没有看清最多只有两个附件,读者可以思考多个附件的情况怎么做。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 70, M = 5e4 + 10;
int n, m, w[N], v[N], f[M], g[M], vis[N], ans;
vector <int> e[N];
int main()
{
cin >> m >> n;
int x, y, z;
for(int i = 1; i <= n; i ++)
{
cin >> x >> y >> z;
w[i] = x * y, v[i] = x;
if(z) e[z].push_back(i);
vis[i] = z;
}
for(int i = 1; i <= n; i ++)
{
if(vis[i]) continue;
for(int j = m; j >= 0; j --) g[j] = f[j];
for(int j = m; j >= v[i]; j --)
{
f[j] = max(f[j], g[j - v[i]] + w[i]);
if(e[i].size() == 1 && j >= v[i] + v[e[i][0]])
{
f[j] = max(f[j], g[j - v[i] - v[e[i][0]]] + w[i] + w[e[i][0]]);
}
if(e[i].size() == 2)
{
if(j >= v[i] + v[e[i][0]]) f[j] = max(f[j], g[j - v[i] - v[e[i][0]]] + w[i] + w[e[i][0]]);
if(j >= v[i] + v[e[i][1]]) f[j] = max(f[j], g[j - v[i] - v[e[i][1]]] + w[i] + w[e[i][1]]);
if(j >= v[i] + v[e[i][0]] + v[e[i][1]]) f[j] = max(f[j], g[j - v[i] - v[e[i][0]] - v[e[i][1]]] + w[i] + w[e[i][0]] + w[e[i][1]]);
}
}
}
for(int i = 0; i <= m; i ++) ans = max(ans, f[i]);
cout << ans << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效