题目链接:http://codeforces.com/problemset/problem/543/B
题意:总共有n个点(1<= n<=3000),m条边(m <= min(3000,n * (n - 1) / 2)),任意两点间只有一条边,边的长度都为1,一开始的时候所有点都能通过某些路径到达。要你求最多删除几条边,能使s1到t1的距离不超过l1,s2到t2的距离不超过l2,如果情况不存在,输出-1,否则输出最多删的边的条数。
思路:看到点的个数比较少,可以先把所有i到j的最短路算出来,并用邻接矩阵存起来,O(n^2)复杂度。接下来只要考虑两种情况:1.s1到t1和s2到t2的路径中没有重合的边,那么只要单独算两条路径的最短路即可。2.从两条路径的一个端点出发,路径中会出现先没有边重合,后来有边重合,最后又没有边重合的情况。对于第2种情况,只要枚举重合路径的两个端点,然后不断更新答案就可以了。
另外需要特别注意的是,两个路径可能是从s1,s2出发(t1,t2也一样。。。)或者是s1,t2出发(s2,t1也一样。。。),这是两种不同的情况要分开考虑,所以还要swap(s1,t1)才能AC。。。。
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 3005;
int dis[maxn][maxn];
vector<int> vec[maxn];
int n,m;
void bfs(int x)
{
queue<int> que;
dis[x][x] = 0;
que.push(x);
while(!que.empty())
{
int temp = que.front();
que.pop();
for(int i = 0;i < vec[temp].size();i++)
{
int v = vec[temp][i];
if(dis[x][v] == -1)
{
dis[x][v] = dis[x][temp] + 1;
que.push(v);
}
}
}
}
int s[2],t[2],l[2];
void show()
{
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
cout<<dis[i][j]<<" ";
cout<<endl;
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
mem(dis,-1);
for(int i = 0;i <= n + 1;i++)vec[i].clear();
for(int i = 0,a,b;i < m;i++)
{
scanf("%d%d",&a,&b);
vec[a].push_back(b);
vec[b].push_back(a);
}
for(int i = 1;i <= n;i++)
bfs(i);
scanf("%d%d%d%d%d%d",&s[0],&t[0],&l[0],&s[1],&t[1],&l[1]);
int ans = 999999999;
for(int oper = 0;oper < 2;oper++)
{
swap(s[1],t[1]);
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
{
int v[] = {dis[s[0]][i] + dis[i][j] + dis[j][t[0]],dis[s[1]][i] + dis[i][j] + dis[j][t[1]]};
if(v[0] <= l[0] && v[1] <= l[1])
{
ans = min(ans,v[0] + v[1] - dis[i][j]);
}
}
}
if(dis[s[0]][t[0]] <= l[0] && dis[s[1]][t[1]] <= l[1])
ans = min(ans,dis[s[0]][t[0]] + dis[s[1]][t[1]]);
if(ans > m)
ans = -1;
else
ans = m - ans;
printf("%d\n",ans);
}
}