Problem C: 先有durong后有天
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 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)
第一行有两个整数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;
}