大概题意:n个点(<=100)m条边(<=1000)的无向图,每个点有消耗costp和价值moneyp,每条边有消耗coste。问从点s出发到点e,消耗不超过t(<=300)所获得的最大价值。经过边必有消耗coste;经过点时,当取得价值moneyp时消耗costp,即为visit该点;当取得价值0,时消耗也为0,即为pass该点。visit的点的moneyp值必须是严格升序。
解法:
容易看出应该用spfa和dp来解。关键时对visit和pass点的处理。
通过floyd预处理出visit每个点对之间的最小边消耗。然后,加一个超级源点和一个超级终点。超级源点负责pas点s能够到达的点,超级终点负责那些能越过e的点
由于visit的点的moneyp值必须严格升序所以也可以拓扑之后dp
不能用dij,因为本题时求最长路,且有正边。需用spfs
注意:
(1)spfa中跑的状态都是合理的,所以可以初始化dp为0;而dp中枚举的状态不一定是合理的,所以要初始化dp为-1
(2)spfa的两种写法,
先建图在跑spfa。
在跑spfa的过程中判断边。
坑点:本题卡vector,要用手写的链表或邻接矩阵
spfa:
//#pragma warning (disable: 4786) //#pragma comment (linker, "/STACK:16777216") //HEAD #include <cstdio> #include <ctime> #include <cstdlib> #include <cstring> #include <queue> #include <string> #include <set> #include <stack> #include <map> #include <cmath> #include <vector> #include <iostream> #include <algorithm> using namespace std; #define REP(i, N) for(int i = 0; i < (N); ++i) #define CLR(A,value) memset(A,value,sizeof(A)) #define RI(n) scanf("%d", &n) #define RII(n, m) scanf("%d%d", &n, &m) #define RIII(n, m, k) scanf("%d%d%d", &n, &m, &k) #define RS(s) scanf("%s", s) typedef long long LL; const int INF = 0x3f3f3f3f; const double eps = 1e-10; const int maxn = 111 * 310; int cost[111], money[111]; int d[111][111]; int dp[111][310]; int inq[111][310]; void floyed(int n) { REP(k, n) REP(i, n) REP(j, n) { d[i][j] = min(d[i][j], d[i][k] + d[k][j]); } } int n, m, t, s, e; int spfa(int ss, int ee) { queue<int>qu; queue<int>qct; memset(dp, 0, sizeof(dp)); memset(inq, 0, sizeof(inq)); qu.push(ss); qct.push(0); inq[ss][0] = 1; int ans = 0; while (!qu.empty()) { int u = qu.front(); qu.pop(); int uct = qct.front(); qct.pop(); inq[u][uct] = 0; if (u == ee) ans = max(ans, dp[u][uct]); for (int v = 0; v < n; v++) { if (v == u || (money[u] >= money[v] && v != ee)) continue; int vct = uct + d[u][v] + cost[v]; if (vct <= t && dp[v][vct] < dp[u][uct] + money[v]) { dp[v][vct] = dp[u][uct] + money[v]; if (!inq[v][vct]) { qu.push(v); qct.push(vct); inq[v][vct] = 1; } } } } return ans; } int main () { int test; RI(test); int ncase = 1; while (test--) { scanf("%d%d%d%d%d", &n, &m, &t, &s, &e); memset(d, 0x3f, sizeof(d)); for (int i = 0; i < n; i++) RI(cost[i]); for (int i = 0; i < n; i++) RI(money[i]); for (int i = 0; i < n + 2; i++) d[i][i] = 0; for (int i = 0; i < m; i++) { int x, y, z; scanf("%d%d%d", &x, &y, &z); d[x][y] = min(d[x][y], z); d[y][x] = d[x][y]; } floyed(n); int ss = n; int ee = n + 1; cost[ss] = money[ss] = 0; cost[ee] = money[ee] = 0; for (int i = 0; i < n; i++) if (d[s][i] != INF) d[ss][i] = d[s][i]; for (int i = 0; i < n; i++) if (d[i][e] != INF) d[i][ee] = d[i][e]; d[ss][ee] = d[s][e]; n += 2; int ans = spfa(ss, ee); printf("Case #%d:\n", ncase++); printf("%d\n", ans); } return 0; }
另种,先建图在跑spfa:
//#pragma warning (disable: 4786) //#pragma comment (linker, "/STACK:16777216") //HEAD #include <cstdio> #include <ctime> #include <cstdlib> #include <cstring> #include <queue> #include <string> #include <set> #include <stack> #include <map> #include <cmath> #include <vector> #include <iostream> #include <algorithm> using namespace std; #define REP(i, N) for(int i = 0; i < (N); ++i) #define CLR(A,value) memset(A,value,sizeof(A)) #define RI(n) scanf("%d", &n) #define RII(n, m) scanf("%d%d", &n, &m) #define RIII(n, m, k) scanf("%d%d%d", &n, &m, &k) #define RS(s) scanf("%s", s) typedef long long LL; const int INF = 0x3f3f3f3f; const double eps = 1e-10; const int maxn = 111 * 310; struct Edge{ int from, to, dist; }; struct BellmanFord { int n, m; int head[maxn]; int next[maxn * 110]; int to[maxn * 110]; int dist[maxn * 110]; int tot; bool inq[maxn]; int d[maxn]; void init(int n) { this->n = n; tot = 0; memset(head, -1, sizeof(head)); } void AddEdge(int ufrom, int uto, int udist) { to[tot] = uto; dist[tot] = udist; next[tot] = head[ufrom]; head[ufrom] = tot++; } bool spfa(int ss) { queue<int>Q; memset(inq, 0, sizeof(inq)); memset(d, 0, sizeof(d)); d[ss] = 0; inq[ss] = 1; Q.push(ss); while (!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = false; for (int i = head[u]; ~i; i = next[i]) // for (int i = 0; i < G[u].size(); i++) { int uto = to[i]; int udist = dist[i]; // Edge& e = edges[G[u][i]]; if (d[uto] < d[u] + udist) { d[uto] = d[u] + udist; if (!inq[uto]) { Q.push(uto); inq[uto] = true; } } } } return false; } }bm; int cost[111], money[111]; int d[111][111]; void floyed(int n) { REP(k, n) REP(i, n) REP(j, n) { d[i][j] = min(d[i][j], d[i][k] + d[k][j]); } } int n, m, t, s, e; int ID(int x, int y) { return x * (t + 1) + y; } int main () { int test; RI(test); int ncase = 1; while (test--) { scanf("%d%d%d%d%d", &n, &m, &t, &s, &e); memset(d, 0x3f, sizeof(d)); for (int i = 0; i < n; i++) d[i][i] = 0; for (int i = 0; i < n; i++) RI(cost[i]); for (int i = 0; i < n; i++) RI(money[i]); for (int i = 0; i < m; i++) { int x, y, z; scanf("%d%d%d", &x, &y, &z); d[x][y] = min(d[x][y], z); d[y][x] = d[x][y]; } floyed(n); int ss = n * (t + 1); int ee = ss + 1; bm.init(ee + 1); for (int i = 0; i < n; i++) if (d[s][i] + cost[i] <= t) bm.AddEdge(ss, ID(i, d[s][i] + cost[i]), money[i]); bm.AddEdge(ss, ee, 0); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i == j || money[i] >= money[j] || d[i][j] == INF) continue; for (int tt = 0; tt + d[i][j] + cost[j] <= t; tt++) { bm.AddEdge(ID(i, tt), ID(j, tt + d[i][j] + cost[j]), money[j]); } } } for (int i = 0; i < n; i++) if (d[i][e] != INF) for (int tt = 0; tt + d[i][e] <= t; tt++) bm.AddEdge(ID(i, tt), ee, 0); bm.spfa(ss); int ans = bm.d[ee]; printf("Case #%d:\n", ncase++); printf("%d\n", ans); } return 0; }
dp解法:
先拓扑,在dp
//#pragma warning (disable: 4786) //#pragma comment (linker, "/STACK:16777216") //HEAD #include <cstdio> #include <ctime> #include <cstdlib> #include <cstring> #include <queue> #include <string> #include <set> #include <stack> #include <map> #include <cmath> #include <vector> #include <iostream> #include <algorithm> using namespace std; #define REP(i, N) for(int i = 0; i < (N); ++i) #define CLR(A,value) memset(A,value,sizeof(A)) #define RI(n) scanf("%d", &n) #define RII(n, m) scanf("%d%d", &n, &m) #define RIII(n, m, k) scanf("%d%d%d", &n, &m, &k) #define RS(s) scanf("%s", s) typedef long long LL; const int INF = 0x3f3f3f3f; const double eps = 1e-10; const int maxn = 111 * 310; int cost[111], money[111]; int d[111][111]; int dp[111][310]; void floyed(int n) { REP(k, n) REP(i, n) REP(j, n) { if (d[i][k] == INF || d[k][j] == INF) continue; d[i][j] = min(d[i][j], d[i][k] + d[k][j]); } } int n, m, t, s, e; int id[111]; int solve() { int ans = 0; memset(dp, -1, sizeof(dp));///不能到达的为-1 for (int i = 0; i < n; i++) { int ct = d[s][i] + cost[i]; if (ct <= t) { dp[i][ct] = money[i]; if (ct + d[i][e] <= t) ans = max(ans, dp[i][ct]); } } for (int ii = 0; ii < n; ii++) { int i = id[ii]; for (int j = 0; j < n; j++) { if (i == j || money[i] >= money[j] || d[i][j] == INF) continue; for (int tt = 0; tt + d[i][j] + cost[j] <= t; tt++) { if (dp[i][tt] == -1) continue; int ct = tt + d[i][j] + cost[j]; if (dp[j][ct] < dp[i][tt] + money[j]) { dp[j][ct] = dp[i][tt] + money[j]; if (ct + d[j][e] <= t) ans = max(ans, dp[j][ct]); } } } } return ans; } bool cmp(int x, int y) { return money[x] < money[y]; } int main () { int test; RI(test); int ncase = 1; while (test--) { scanf("%d%d%d%d%d", &n, &m, &t, &s, &e); memset(d, INF, sizeof(d)); for (int i = 0; i < n; i++) d[i][i] = 0, id[i] = i; for (int i = 0; i < n; i++) RI(cost[i]); for (int i = 0; i < n; i++) RI(money[i]); for (int i = 0; i < m; i++) { int x, y, z; scanf("%d%d%d", &x, &y, &z); d[x][y] = min(d[x][y], z); d[y][x] = d[x][y]; } floyed(n); sort(id, id + n, cmp); int ans = solve(); printf("Case #%d:\n", ncase++); printf("%d\n", ans); } return 0; }
HDU 4571 Travel in time (SPFA 或 dp)
时间: 2024-12-18 14:27:42