[BZOJ1927][SDOI2010][最小费用最大流]星际竞速

[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));
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值