CSUST选拔赛题解之-Problem C: 先有durong后有天

Problem C: 先有durong后有天

Time Limit: 3 Sec   Memory Limit: 128 MB
Submit: 59   Solved: 16
[ Submit][ Status][ Web Board]

Description

durong同学最近斥巨资买了一座岛,这座岛上有n个部落,durong同学在这n个部落之间修了m条道路,使得任意两个部落都能互相到达,不过最近durong
有点缺钱,所以他打算卖掉一些道路(也就是拆掉这些道路,每条道路都能卖一个金币),不过拆掉一些道路后还必须满足从s1到e1的时间不超过t1,从s2到e2
的时间不超过t2这两个条件,求最多能卖得多少金币

(每条路长度也是1)

Input

多组输入
第一行有两个整数n,m(1 ≤ n ≤ 3000, n-1 ≤ m ≤ min(3000,n*(n-1)/2) )。
接下来m行,每行有两个整数 ai, bi (1 ≤ ai, bi ≤ n, ai ≠ bi),表示ai和bi之间有一条道路。
输入保证是一个连通图。
最后两行每行有三个整数s1, e1, t1和 s2, e2, t2, (1 ≤ si, ei ≤ n, 0 ≤ ti ≤ n)

Output

输出一个整数,表示最多可以破坏的道路数目,如果没有解,输出-1。

Sample Input

5 4
1 2
2 3
3 4
4 5
1 3 2
3 5 2

Sample Output

0

HINT


解题思路:要求能卖金币的最大数,即要我们求出最多能拆多少条路,那我们分别求出s1-e1,s2-e2的最短路径即可,然后再用 总路径长度减去它们,但是可能这两条路有共同的一段路经,因此有可能例外这两条路的长度 减去交叉的长度 比那两条最短路的总长度更短,因为数据不是很大,所以我们可以暴力枚举O(n^2)下公共部分,算出最终的最小值,因此我们在求最短路的时候要求出任意两点的最短路这样才能在枚举时保证距离是最短的,因为每条路的距离都为1,因此我们就可以bfs一下就可以解决了O (n^2)。最后还有一个很难想到的情况: 就是这这些路都是无向的,因此在枚举时注意枚举两个方向!!!


AC代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<math.h>
#include<time.h>
#include<map>
#include<string>
#include<algorithm>
#include<set>
#include<queue>
#define N 3010

const int INF = 1e9;

using namespace std;

int st[3], en[3], t[3], cnt;
int dist[N][N];
int head[N], vis[N];
int n, m, x, y, z, len, flag;

struct Edge
{
    int End;
    int next;
}e[2*N];

void Add_Edge(int u, int v) //用邻接表实现每条路的连通
{
    e[cnt].End = u;
    e[cnt].next = head[v];
    head[v] = cnt ++;
}

void bfs()
{
    for(int i = 1; i <= n; i ++) { //求每两点的最短路
        dist[i][i] = 0;
        memset(vis, 0, sizeof(vis));
        queue<int> q;
        q.push(i);
        vis[i] = 1;
        while(!q.empty()) {
            int cur = q.front();
            q.pop();
            for(int j = head[cur]; j != -1; j = e[j].next) {
                int v = e[j].End;
                if(vis[v]) continue;
                int next = v;
                dist[i][v] = dist[i][cur] + 1;
                q.push(next);
                vis[v] = 1;
            }
        }
    }
}

int main()
{
    while(~scanf("%d%d", &n, &m))
    {
        flag = 0, len = 0;
        cnt = 0;
        memset(head, -1, sizeof(head));
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= n; j ++)
                dist[i][j] = INF;
        for(int i = 0; i < m; i ++) {
            scanf("%d%d", &x, &y);
            Add_Edge(x, y);
            Add_Edge(y, x);
        }
        bfs();
        for(int i = 1; i <= 2; i ++) {
            scanf("%d%d%d", &st[i], &en[i], &t[i]);
            if(t[i] < dist[st[i]][en[i]]) flag = 1; //不能超过限制条件
            len += dist[st[i]][en[i]];
        }
        if(flag) {
            printf("-1\n"); continue;
        }
        for(int i = 1; i <= n; i ++) //枚举交叉路段
            for(int j = 1; j <= n ;j ++) {
                x = dist[st[1]][i] + dist[i][j] + dist[j][en[1]];
                y = dist[st[2]][i] + dist[i][j] + dist[j][en[2]]; //y、z为两个方向
                z = dist[en[2]][i] + dist[i][j] + dist[j][st[2]];
                if(x <= t[1] && y <= t[2]) //不能超过限制条件
                    len = min(len, x + y - dist[i][j]);
                if(x <= t[1] && z <= t[2])
                    len = min(len, x + z - dist[i][j]);
            }
        printf("%d\n", m - len);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值