SDOI2009晨跑题解

  • Description
    Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑、仰卧起坐等 等,不过到目前为止,他坚持下来的只有晨跑。 现在给出一张学校附近的地图,这张地图中包含N个十字路口和M条街道,Elaxia只能从 一个十字路口跑向另外一个十字路口,街道之间只在十字路口处相交。Elaxia每天从寝室出发跑到学校,保证寝室编号为1,学校编号为N。Elaxia的晨跑计划是按周期(包含若干天)进行的,由于他不喜欢走重复的路线,所以在一个周期内,每天的晨跑路线都不会相交(在十字路口处),寝室和学校不算十字路口。Elaxia耐力不太好,他希望在一个周期内跑的路程尽量短,但是又希望训练周期包含的天数尽量长。除了练空手道,Elaxia其他时间都花在了学习和找MM上面,所有他想请你帮忙为他设计一套满足他要求的晨跑计划。

  • Input
    第一行:两个数N,M。表示十字路口数和街道数。 接下来M行,每行3个数a,b,c,表示路口a和路口b之间有条长度为c的街道(单向)

  • Output
    两个数,第一个数为最长周期的天数,第二个数为满足最长天数的条件下最短的路程长 度。

  • Sample Input
    7 10
    1 2 1
    1 3 1
    2 4 1
    3 4 1
    4 5 1
    4 6 1
    2 5 5
    3 6 6
    5 7 1
    6 7 1

  • Sample Output
    2 11

  • HINT
    对于30%的数据,N ≤ 20,M ≤ 120。
    对于100%的数据,N ≤ 200,M ≤ 20000。

  • Solution
    网络流老题了,不过今天才开始做。要求不走重复的路口,只要把路口拆成容量为1的边即可。其余的按照题目要求添边,容量均为1,(路口都是1了,街道是多少就无所谓了)费用为路长。一遍费用流下来最大流就是最长周期,最小费用则是最短路程。

  • Code

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxm = 100000, maxn = 50000, INF = 707406378;
int u[maxm], v[maxm], w[maxm], cost[maxm], next[maxm], pre[maxm], other[maxm];
int point[maxn], d[maxn];
int k, s, t, ans, mc, n, m;
bool vis[maxn];
inline void read(int &a) {
    int f = 1;
    char ch = getchar();
    a = 0;
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        a = a*10 + ch - 48;
        ch = getchar();
    }
    a *= f;
}
inline void write(int a) {
    int top = 0;
    char ch[30];
    if(a < 0) { putchar('-'); a = -a; }
    do {
        ch[top++] = a%10 + 48;
        a /= 10;
    } while(a);
    while(top--) putchar(ch[top]);
}
inline void addedge(int a, int b, int c,int co) {
    u[++k] = a; v[k] = b; w[k] = c; cost[k] = co;
    next[k] = point[a]; point[a] = k; other[k] = k + 1;
    u[++k] = b; v[k] = a; w[k] = 0; cost[k] = -co;
    next[k] = point[b]; point[b] = k; other[k] = k - 1;
}
inline bool spfa() {
    queue <int> q;
    memset(vis, false, sizeof(vis));
    memset(d, 127/3, sizeof(d));
    memset(pre, 0, sizeof(pre));
    q.push(s); vis[s] = true; d[s] = 0;
    int o, j;
    while(!q.empty()) {
        o = q.front(); j = point[o];
        q.pop(); vis[o] = false;
        while(j != 0) {
            if(d[u[j]] + cost[j] < d[v[j]] && w[j] > 0) {
                d[v[j]] = d[u[j]] + cost[j];
                pre[v[j]] = j;
                if(!vis[v[j]]) { vis[v[j]] = true; q.push(v[j]); }
            }
            j = next[j];
        }
    }
    if(d[t] < INF) return true;
    else return false;
}
inline void augment() {
    int p = t, flow = INF;
    while(p != s) {
        flow =  min(flow, w[pre[p]]);
        p = u[pre[p]];
    }
    ans += flow; mc += d[t]*flow; p = t;
    while(p != s) {
        w[pre[p]] -= flow;
        w[other[pre[p]]] += flow;
        p = u[pre[p]];
    }
}
int main() {
    int a, b, c, o, num[300];
    memset(num, 0, sizeof(num));
    read(n); read(m);
    for(int i = 2; i <= n; ++i) num[i] = i + (m<<1);
    s = 1; t = n; num[s] = s;
    for(int i = 1; i < n; ++i) { addedge(i, num[i], 1, 0); }
    for(int i = 1; i <= m; ++i) {
        read(a); read(b); read(c);
        addedge(num[a], b, 1, c);
    }
    while(spfa()) augment();
    write(ans); putchar(' '); write(mc);
    //bzoj令人欲撞南墙的PE导致了如此奇怪的输出方式的诞生
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值