[Problem Description]
10 年一度的银河系赛车大赛又要开始了。作为全银河最盛大的活动之一, 夺得这个项目的冠军无疑是很多人的梦想,来自杰森座 α星的悠悠也是其中之一。 赛车大赛的赛场由 N 颗行星和M条双向星际航路构成,其中每颗行星都有 一个不同的引力值。大赛要求车手们从一颗与这 N 颗行星之间没有任何航路的 天体出发,访问这 N 颗行星每颗恰好一次,首先完成这一目标的人获得胜利。 由于赛制非常开放,很多人驾驶着千奇百怪的自制赛车来参赛。这次悠悠驾 驶的赛车名为超能电驴,这是一部凝聚了全银河最尖端科技结晶的梦幻赛车。作 为最高科技的产物,超能电驴有两种移动模式:高速航行模式和能力爆发模式。 在高速航行模式下,超能电驴会展开反物质引擎,以数倍于光速的速度沿星际航 路高速航行。在能力爆发模式下,超能电驴脱离时空的束缚,使用超能力进行空 间跳跃——在经过一段时间的定位之后,它能瞬间移动到任意一个行星。 天不遂人愿,在比赛的前一天,超能电驴在一场离子风暴中不幸受损,机能 出现了一些障碍:在使用高速航行模式的时候,只能由每个星球飞往引力比它大 的星球,否则赛车就会发生爆炸。 尽管心爱的赛车出了问题,但是悠悠仍然坚信自己可以取得胜利。他找到了 全银河最聪明的贤者——你,请你为他安排一条比赛的方案,使得他能够用最少 的时间完成比赛。
[Algorithm]
最小费用最大流
[Analysis]
乍一看好像最短路,但由于要保证每个点只经过一次,所以考虑网络流模型。建图如下
1.S -> i cap = 1, cost = Ai
2.S -> i + n cap = 1, cost = 0
3.i + n -> j cap = 1, cost = dis[i][j]
(i < j && i, j 有边)
4.i -> T cap = 1, cost = 0
建图4保证每个边访问一次,由于经过一个点流量会流向t,所以从s再给每个点补充流量,即建图2。建图1为空间跳跃,建图3为高速航行。
[Code]
/**************************************************************
Problem: 1927
User: gaotianyu1350
Language: C++
Result: Accepted
Time:4212 ms
Memory:2180 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <iostream>
using namespace std;
const int MAXN = 2000;
const int MAXM = 45000;
const int INF = 2e9;
int point[MAXN], next[MAXM], v[MAXM], flow[MAXM], cap[MAXM], w[MAXM];
int lastedge[MAXN], dis[MAXN];
bool inq[MAXN];
int tot;
int n, m;
inline void init()
{
memset(point, -1, sizeof(point));
memset(next, -1, sizeof(next));
tot = -1;
}
inline void addedge(int x, int y, int theCap, int theDis)
{
tot++;
next[tot] = point[x]; point[x] = tot; v[tot] = y;
flow[tot] = 0; cap[tot] = theCap; w[tot] = theDis;
tot++;
next[tot] = point[y]; point[y] = tot; v[tot] = x;
flow[tot] = 0; cap[tot] = 0; w[tot] = -theDis;
}
inline int addflow(int s, int t)
{
int ans = INF;
int now = t;
while (now != s)
{
int temp = lastedge[now];
ans = min(ans, cap[temp] - flow[temp]);
now = v[temp ^ 1];
}
now = t;
while (now != s)
{
flow[lastedge[now]] += ans;
flow[lastedge[now] ^ 1] -= ans;
now = v[lastedge[now] ^ 1];
}
return ans;
}
bool spfa(int s, int t, int &maxflow, int &mincost)
{
queue<int> q;
memset(dis, 0x7f, sizeof(dis));
memset(inq, 0, sizeof(inq));
dis[s] = 0; inq[s] = true; q.push(s);
while (!q.empty())
{
int now = q.front(); q.pop();
inq[now] = false;
for (int temp = point[now] ; temp != -1; temp = next[temp])
if (flow[temp] < cap[temp] && dis[now] + w[temp] < dis[v[temp]])
{
dis[v[temp]] = dis[now] + w[temp];
lastedge[v[temp]] = temp;
if (!inq[v[temp]])
q.push(v[temp]), inq[v[temp]] = true;
}
}
if (dis[t] > INF) return false;
int add = addflow(s, t);
maxflow += add;
mincost += add * dis[t];
return true;
}
inline int solve(int s, int t)
{
int maxflow = 0, mincost = 0;
while (spfa(s, t, maxflow, mincost));
/*{
int tiaoshi = 0;
tiaoshi++;
}*/
return mincost;
}
int main()
{
//freopen("input.txt", "r", stdin);
init();
scanf("%d%d", &n, &m);
int start = 2 * n + 1, end = 2 * n + 2;
for (int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
addedge(start, i, 1, x);
addedge(start, i + n, 1, 0);
addedge(i, end, 1, 0);
}
for (int i = 1; i <= m; i++)
{
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
if (x > y)
{
int temp = x; x = y; y = temp;
}
addedge(x + n, y, 1, z);
}
printf("%d\n", solve(start, end));
}