BZOJ1927 [Sdoi2010]星际竞速
题目大意:自己看中文。
。
。
思路:不难发现,题目等价于让我们求出一些标号上升的子序列精确覆盖全集。每个子序列的起点一定是利用“能力爆发” 得到的。
那么我们仅仅需对于每一个星球,确定一个前驱就能够了。
若是0作为前驱。则转移代价为定位时间;否则转移代价为路径长度。
此外一个点的前驱的标号严格小于自己的标号。
注意0能够作为多个星球的前驱,剩下的星球仅仅能作为一个星球的前驱。
于是转化为类二分图最优匹配问题,利用最小费用流求解就可以。
Code:
#include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; queue<int> q; #define N 810 struct Solver { int head[N<<1], next[N*N], end[N*N], flow[N*N], cost[N*N], ind; int d[N<<1], ld[N<<1], lb[N<<1]; bool inq[N<<1]; void reset() { ind = 0; memset(head, -1, sizeof head); } void addedge(int a, int b, int _flow, int _cost) { int q = ind++; end[q] = b; next[q] = head[a]; head[a] = q; flow[q] = _flow; cost[q] = _cost; } void make(int a, int b, int _flow, int _cost) { addedge(a, b, _flow, _cost); addedge(b, a, 0, -_cost); } bool spfa(int S, int T) { memset(d, 0x3f, sizeof d); d[S] = 0; inq[S] = 1; q.push(S); int i, j; while(!q.empty()) { i = q.front(); q.pop(); inq[i] = 0; for(j = head[i]; j != -1; j = next[j]) { if (flow[j] && d[end[j]] > d[i] + cost[j]) { d[end[j]] = d[i] + cost[j]; ld[end[j]] = i; lb[end[j]] = j; if (!inq[end[j]]) inq[end[j]] = 1, q.push(end[j]); } } } return d[T] != 0x3f3f3f3f; } int Mincost(int S, int T) { int res = 0, Min, i; while(spfa(S, T)) { Min = 0x3f3f3f3f; for(i = T; i != S; i = ld[i]) Min = Min > flow[lb[i]] ? flow[lb[i]] : Min; for(i = T; i != S; i = ld[i]) flow[lb[i]] -= Min, flow[lb[i] ^ 1] += Min; res += Min * d[T]; } return res; } }G; int main() { int n, m; scanf("%d%d", &n, &m); register int i; int x; G.reset(); for(i = 1; i <= n; ++i) { scanf("%d", &x); G.make(1, i<<1^1, 1, x); } int a, b; for(i = 1; i <= m; ++i) { scanf("%d%d%d", &a, &b, &x); if (a > b) swap(a, b); G.make(a<<1, b<<1^1, 1, x); } G.make(0, 1, 0x3f3f3f3f, 0); for(i = 1; i <= n; ++i) G.make(0, i<<1, 1, 0), G.make(i<<1^1, 2*n+2, 1, 0); printf("%d\n", G.Mincost(0, 2*n+2)); return 0; }