[BZOJ1266][AHOI2006][最短路][最小割]上学路线

[Problem Description]
可可和卡卡家住合肥市的东郊,每天上学他们都要转车多次才能到达市区西端的学校。直到有一天他们两人参加了学校的信息学奥林匹克竞赛小组才发现每天上学的乘车路线不一定是最优的。 可可:“很可能我们在上学的路途上浪费了大量的时间,让我们写一个程序来计算上学需要的最少时间吧!” 合肥市一共设有N个公交车站,不妨将它们编号为1…N的自然数,并认为可可和卡卡家住在1号汽车站附近,而他们学校在N号汽车站。市内有M条直达汽车路线,执行第i条路线的公交车往返于站点pi和qi之间,从起点到终点需要花费的时间为ti。(1<=i<=M, 1<=pi, qi<=N) 两个人坐在电脑前,根据上面的信息很快就编程算出了最优的乘车方案。然而可可忽然有了一个鬼点子,他想趁卡卡不备,在卡卡的输入数据中删去一些路线,从而让卡卡的程序得出的答案大于实际的最短时间。而对于每一条路线i事实上都有一个代价ci:删去路线的ci越大卡卡就越容易发现这个玩笑,可可想知道什么样的删除方案可以达到他的目的而让被删除的公交车路线ci之和最小。 [任务] 编写一个程序:  从输入文件中读取合肥市公交路线的信息;  计算出实际上可可和卡卡上学需要花费的最少时间;  帮助可可设计一个方案,删除输入信息中的一些公交路线,使得删除后从家到学校需要的最少时间变大,而被删除路线的ci和最小;向输出文件输出答案。
[Algorithm]
最短路 最小割
[Analysis]
求最短路不说了……求删除路线代价最小,方法是确定哪些边可以成为最短路上的边,把这些边建成一个图,然后求最小割跑网络流……建图的方法有很多种,我是用广搜的方法,先将终点入队。如果当前点引出的边所到的点的dis加上边权等于当前点的dis,则加入该边,引出的点入队。
[Pay Attention]
要注意网络流建图的时候建的是单向的边,因为走最短路的时候只可能从起点的那一头走向终点。刚开始我没有理解清楚建图的含义结果建了双向的……
[Code]
/**************************************************************
    Problem: 1266
    User: gaotianyu1350
    Language: C++
    Result: Accepted
    Time:300 ms
    Memory:16920 kb
****************************************************************/
 
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <iostream>
using namespace std;
 
const int MAXN = 1010;
const int MAXM = 400000;
const int INF  = 2147483640;
 
int opoint[MAXM], onext[MAXM], ov[MAXM], ow[MAXM], oc[MAXM];
int point[MAXM], next[MAXM], v[MAXM], flow[MAXM], cap[MAXM];
int tot, otot;
int n, m;
 
bool inq[MAXN];
int dis[MAXN];
int cur[MAXN], lastedge[MAXN], deep[MAXN], num[MAXN];
 
inline void init()
{
    memset(opoint, -1, sizeof(point));
    memset(onext, -1, sizeof(onext));
    memset(point, -1, sizeof(point));
    memset(next, -1, sizeof(next));
    tot = -1; otot = -1;
}
 
inline void oaddedge(int x, int y, int length, int cost)
{
    otot++;
    onext[otot] = opoint[x]; 
    opoint[x] = otot;
    ov[otot] = y;
    ow[otot] = length;
    oc[otot] = cost;
}
 
inline void addedge(int x, int y, int theCap)
{
    tot++;
    next[tot] = point[x]; 
    point[x] = tot;
    v[tot] = y;
    flow[tot] = 0;
    cap[tot] = theCap;
}
 
inline int spfa()
{
    memset(inq, 0, sizeof(inq));
    memset(dis, 0x7f, sizeof(dis));
    dis[1] = 0; inq[1] = true;
    queue<int> q;
    while (!q.empty()) q.pop();
    q.push(1);
    while (!q.empty())
    {
        int now = q.front(); q.pop();
        inq[now] = false;
        for (int temp = opoint[now]; temp != -1; temp = onext[temp])
            if (dis[now] + ow[temp] < dis[ov[temp]])
            {
                dis[ov[temp]] = dis[now] + ow[temp];
                if (!inq[ov[temp]])
                {
                    q.push(ov[temp]);
                    inq[ov[temp]] = true;
                }
            }
    }
    while (!q.empty()) q.pop();
    memset(inq, 0, sizeof(inq));
    q.push(n); inq[n] = true;
    while (!q.empty())
    {
        int now = q.front(); q.pop();
        for (int temp = opoint[now]; temp != -1; temp = onext[temp])
            if (dis[ov[temp]] + ow[temp] == dis[now])
            {
                addedge(ov[temp], now, oc[temp]);
                addedge(now, ov[temp], 0);
                if (!inq[ov[temp]])
                {
                    inq[ov[temp]] = true;
                    q.push(ov[temp]);
                }
            }
    }
    return dis[n];
}
 
inline void bfs()
{
    queue<int> q;
    while (!q.empty()) q.pop();
    memset(deep, 0, sizeof(deep));
    memset(inq, 0, sizeof(inq));
    inq[n] = true;
    q.push(n);
    while (!q.empty())
    {
        int now = q.front(); q.pop();
        for (int temp = point[now]; temp != -1; temp = next[temp])
            if (!inq[v[temp]] && flow[temp ^ 1] < cap[temp ^ 1])
            {
                deep[v[temp]] = deep[now] + 1;
                inq[v[temp]] = true;
                q.push(v[temp]);
            }
    }
}
 
inline int addflow()
{
    int add = INF;
    int now = n;
    while (now != 1)
    {
        add = min(add, cap[lastedge[now]] - flow[lastedge[now]]);
        now = v[lastedge[now] ^ 1];
    }
    now = n;
    while (now != 1)
    {
        flow[lastedge[now]] += add;
        flow[lastedge[now] ^ 1] -= add;
        now = v[lastedge[now] ^ 1];
    }
    return add;
}
 
inline int isap()
{
    int ans = 0, now = 1;
    memset(lastedge, 0, sizeof(lastedge));
    memset(num, 0, sizeof(num));
    for (int i = 1; i <= n; i++)
        cur[i] = point[i];
    bfs();
    for (int i = 1; i <= n; i++)
        num[deep[i]]++;
    while (deep[1] < n)
    {
        if (now == n)
        {
            ans += addflow();
            now = 1;
        }
        bool isok = false;
        for (int temp = cur[now]; temp != -1; temp = next[temp])
            if (deep[now] == deep[v[temp]] + 1 && flow[temp] < cap[temp])
            {
                isok = true;
                cur[now] = temp;
                lastedge[v[temp]] = temp;
                now = v[temp];
                break; 
            }
        if (!isok)
        {
            int minn = n - 1;
            for (int temp = point[now]; temp != -1; temp = next[temp])
                if (flow[temp] < cap[temp])
                    minn = min(minn, deep[v[temp]]);
            if (!(--num[deep[now]])) break;
            num[deep[now] = minn + 1]++;
            cur[now] = point[now];
            if (now != 1) now = v[lastedge[now] ^ 1];
        }
    }
    return ans;
}
 
int main()
{
    init();
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++)
    {
        int x, y, length, cost;
        scanf("%d%d%d%d", &x, &y, &length, &cost);
        oaddedge(x, y, length, cost);
        oaddedge(y, x, length, cost);
    }
    int ans = spfa();
    printf("%d\n", ans);
    int lesscost = isap();
    printf("%d\n", lesscost);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值