題目鏈接: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);
}
}