[Luogu P2153] [BZOJ 1877] [SDOI2009] 晨跑

洛谷传送门
BZOJ传送门

题目描述

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

存在 1n 1 → n 的边存在。这种情况下,这条边只能走一次。

输入输出格式

输入格式:

第一行:两个数 N,M N , M 。表示十字路口数和街道数。

接下来 M M 行,每行3个数 a,b,c a , b , c ,表示路口 a a 和路口b之间有条长度为 c c 的街道(单向)。

输出格式:

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

输入输出样例

输入样例#1:

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

输出样例#1:

2 11

说明

对于30%的数据,N20M120

对于100%的数据, N200M20000 N ≤ 200 , M ≤ 20000

解题分析

很明显最小费用最大流嘛…拆点限流, 流量都为 1 1 <script type="math/tex" id="MathJax-Element-15">1</script>, 总之十分套路…

代码如下:

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <queue>
#define INF 100000000
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MD 805
#define ME 20050
template <class TT>
IN void in(TT &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
int dot, line, ans, tot, cnt, S, T;
int head[MD], dis[MD], del[MD], pre[MD];
bool vis[MD];
struct Edge {int to, fl, cost, nex;} edge[ME << 4];
IN void add(R int from, R int to, R int fl, R int cost)
{
    edge[++cnt] = {to, fl, cost, head[from]}, head[from] = cnt;
    edge[++cnt] = {from, 0, -cost, head[to]}, head[to] = cnt;
}
namespace MCMF
{
    std::queue <int> q;
    IN bool SPFA()
    {
        q.push(S); R int now;
        std::memset(dis, 63, sizeof(dis));
        dis[S] = 0; del[S] = INF;
        W (!q.empty())
        {
            now = q.front(); q.pop();
            for (R int i = head[now]; ~i; i = edge[i].nex)
            {
                if(edge[i].fl > 0 && dis[edge[i].to] > dis[now] + edge[i].cost)
                {
                    dis[edge[i].to] = dis[now] + edge[i].cost;
                    pre[edge[i].to] = i;
                    del[edge[i].to] = std::min(del[now], edge[i].fl);
                    if(!vis[edge[i].to]) vis[edge[i].to] = true, q.push(edge[i].to);
                }
            }
            vis[now] = false;
        }
        return dis[T] != dis[0];
    }
    IN void updata()
    {
        ans += del[T];
        tot += del[T] * dis[T];
        R int cur = T, pr;
        W (cur != S)
        {
            pr = pre[cur];
            edge[pr].fl -= del[dot];
            edge[pr ^ 1].fl += del[dot];
            cur = edge[pr ^ 1].to;
        }
    }
    void init() {W (SPFA()) updata();}
}
int main(void)
{
    std::memset(head, cnt = -1, sizeof(head));
    int a, b, c;
    in(dot), in(line); S = 1 + dot, T = dot;
    for (R int i = 1; i <= dot; ++i) add(i, i + dot, 1, 0);
    for (R int i = 1; i <= line; ++i)
    {
        in(a), in(b), in(c);
        add(a + dot, b, 1, c);
    }
    MCMF::init();
    printf("%d %d", ans, tot);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值