自创第一题
题目背景
最好的朋友在身边,最爱的人就在对面.
自爱情公寓拆迁已经过去十多年……
多年过后,曾经爱情公寓的每个人都成为了自己想成为的样子,一菲成为了著作等身的大教授兼弹一闪道馆馆主,小贤主持的新《你的太阳我的心》大火,咖喱酱成为了娱乐圈配音界的扛把子,子乔和美嘉把事业做大了,成为了人生赢家,海棠成为了一代文豪,大力在火星上研究水稻,张伟则跟随大力成为了火星上有名的大律师。这一天,海棠邀请朋友们来参加掘地重工举办的世界博览会,就这样,大家再一次重聚,踏上了世博会之旅……
题目描述
这次博览会一共有\(n\)个展厅,编号从\(1\)到\(n\),且这些展厅是由单向道路连接的,掘地重工的工作人员们对这次博览会的路线进行了精心的设计,使得这次博览会的展厅之间不存在循环路线。
起初,小伙伴们在\(1\)号展厅,这次旅行的终点在\(n\)号展览厅---爱情公寓厅,这是掘地重工给爱情公寓的小伙伴们专门准备的礼物。因为他们想要尽快到达\(n\)号厅而又想尽量多的参观别的展厅,所以他们想在\(t\)个时间单位内到达\(n\)号厅。
假设参观展厅不需要花费时间,现在你的任务就是帮助他们在不超过\(t\)的时间内确定从\(1\)号展厅到\(n\)号展厅的旅程中最多可以参观多少个展厅。保证至少有一条从\(1\)号展厅到\(n\)号展厅的路线,并保证这条道路小伙伴们将花费不超过\(t\)个时间单位通过。
输入格式
第一行三个整数\(n, m, t\),分别表示展厅的数量、展厅之间的道路数量以及时间限制。
接下来\(m\)行,每行三个整数\(x,y,w\),表示从\(x\)号展厅到\(y\)号展厅有一条花费时间\(w\)的路线
输出格式
第一行一个整数\(ans\),表示最多可以参观的展厅数量。
第二行\(ans\)个整数,按访问顺序输出路径(即每个访问到的点)
数据范围
对于\(30%\)的数据满足:\(2≤n≤10\),\(1≤m≤10\)
对于\(100%\)的数据满足:\(2≤n≤5000,1≤m≤5000,t≤10^9,1≤w≤10^9\)
\(256MB\)
注意
绝对良心题吧嘻嘻\(qwq\)
小清新简单题,用来休闲
solution
一句话题意
有一个\(n\)个点\(m\)条边的有向无环图,求从\(1\)到\(n\)总路径长度不超过\(t\),且经过点最多的路径。
题解
拓扑排序+\(DP\)
说实话其实不用给部分分……
很显然,题目中给出的图是一个\(DAG\)(有向无环图)
因为\(n\)和\(m\)只有\(5000\),所以我们可以大胆地写一个\(O(nm)\)的算法。
因为要求最短路径,且图中无环,所以我们可以大胆地用\(DP\)来做
用一个二维数组\(f[i][j]\)表示从\(1\)号点到\(i\)号点经过\(j\)个点的最短路径(初始化\(inf\)),最后只要从大到小判断第一个\(f[n][i]\le t\)的\(i\)即可,同时,为了保证每个点都是最小值,\(DP\)要按照拓扑序的顺序(而不是节点序),因为是\(DAG\)嘛~
转移:\(f[x][j] = min(f[x][j], f[x][j - 1]+e[i].val)\)(\(i\)是\(x\)的连边,\(j=[2\)~\(n]\))
再一个问题就是记录路径了,我们可以在\(DP\)过程中用一个类似于\(f\)数组的\(pre\)数组,\(pre[i][j]\)表示从\(1\)走到\(i\),经过了\(j\)个点的上一个节点的编号,输出的时候可以用\(stack\)记录,也可以直接回溯
代码
#include <stack>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int A = 5e3 + 11;
const int INF = 0x3f3f3f3f;
inline int read() {
char c = getchar(); int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
int n, m, d, cnt, head[A];
struct node { int to, nxt, val; } e[A];
int f[A][A], pre[A][A], rdu[A], mx;;
vector <int> s;
stack <int> path;
inline void add(int from, int to, int val) {
e[cnt].to = to;
e[cnt].val = val;
e[cnt].nxt = head[from];
head[from] = cnt++;
}
void topo() { //拓扑排序
queue <int> q;
for (int i = 1; i <= n; i++) if (rdu[i] == 0) q.push(i);
while (!q.empty()) {
int x = q.front();
q.pop();
s.push_back(x);
for (int i = head[x]; i != -1; i = e[i].nxt)
if ((--rdu[e[i].to]) == 0) q.push(e[i].to);
}
}
signed main() {
freopen("5.in", "r", stdin);
freopen("5.out", "w", stdout);
memset(f, INF, sizeof(f));
memset(pre, -1, sizeof(pre));
memset(head, -1, sizeof(head));
n = read(), m = read(), d = read();
for (int i = 1, x, y, z; i <= m; i++) {
x = read(), y = read(), z = read();
add(x, y, z);
rdu[y]++;
}
topo();
f[1][1] = 0;
for (int k = 0; k < s.size(); k++) {
int x = s[k];
for (int i = head[x]; i != -1; i = e[i].nxt)
for (int j = 2; j <= n; j++)
if (f[x][j - 1] + e[i].val < f[e[i].to][j]) {
f[e[i].to][j] = f[x][j - 1] + e[i].val;
pre[e[i].to][j] = x;
}
}
for (int i = n; i >= 1; i--)
if (f[n][i] <= d) {
mx = i;
break;
}
cout << mx << '\n';
int pos = n, dep = mx;
while (pos != -1) {
path.push(pos);
pos = pre[pos][dep];
dep--;
}
while (!path.empty()) {
cout << path.top() << ' ';
path.pop();
}
cout << '\n';
return 0;
}