背景:\(Codeforces\) \(Round\) \(\#302\) \((Div. 1)\) \(B\) 题, \(Codeforces543B\)
给定一张边权全为 \(1\) 的图。在保证点 \(s_1\) 到点 \(t_1\) 的距离不超过 \(l_1\) 且点 \(s_2\) 到点 \(t_2\) 的距离不超过 \(l_2\) 的条件下,求最多能删去的边数。如果没有合法方案,输出 \(-1\) 。
这题最关键的就是转化模型。一开始我死都没想到正解就是因为忽略了边权全为 \(1\) 。
设点 \(i\) 到点 \(j\) 的最短长度为 \(dis[i][j]\) 。这时原条件就可以转化成求点 \(s_1\) 到点 \(t_1\) 的最短路上和点 \(s_2\) 到点 \(t_2\) 的最短路上经过的路径边数之和。
当然,这其中有 \(3\) 种情况:
第一种是点 \(s_1\) 到点 \(t_1\) 的最短路和点 \(s_2\) 到点 \(t_2\) 的最短路没有重叠部分。这个时候两个最短路值直接相加就行。
第二种是点 \(s_1\) 到点 \(t_1\) 的最短路和点 \(s_2\) 到点 \(t_2\) 的最短路在点 \(i\) 和点 \(j\) 之间重叠。这个时候可以 \(O(n^2)\) 枚举重叠点 \(i\) 和 \(j\) ,在保证路长限制的情况下更新答案就行,答案是 \(dis[s_1][i]+dis[s_2][i]+dis[i][j]+dis[j][t_1]+dis[j][t_2]\) 。
第三种也是点 \(s_1\) 到点 \(t_1\) 的最短路和点 \(s_2\) 到点 \(t_2\) 的最短路在点 \(i\) 和点 \(j\) 之间重叠,但是点 \(s_2\) 到点 \(t_2\) 的最短路从点 \(i\) 走向点 \(j\) ,而点 \(s_1\) 到点 \(t_1\) 的最短路从点 \(j\) 走向点 \(i\) 。 这个时候做法同上,答案是 \(dis[s_1][j]+dis[s_2][i]+dis[i][j]+dis[i][t_1]+dis[j][t_2]\) 。
至于求任意两点的最短路嘛,做 \(n\) 遍 \(bfs\) 就行。
整个过程就是这样,如果还有什么不明白的,评论区见!感谢您的耐心阅读!
代码如下:
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int ret=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
ret=(ret<<1)+(ret<<3)+ch-'0';
ch=getchar();
}
return ret*f;
}
int n,m,x,y,s1,s2,t1,t2,l1,l2,ans;
int num,head[6005],dis[3005][3005],vis[3005];
struct edge
{
int ver,nxt;
}e[6005];
queue<int> q;
inline void adde(int u,int v)
{
e[++num].ver=v;
e[num].nxt=head[u];
head[u]=num;
}
inline void bfs(int s)
{
memset(vis,0,sizeof(vis));
q.push(s);
vis[s]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
for(register int i=head[x];i;i=e[i].nxt)
{
int y=e[i].ver;
if(vis[y])
continue;
vis[y]=1;
dis[s][y]=dis[s][x]+1;
q.push(y);
}
}
}
int main()
{
n=read();
m=read();
for(register int i=1;i<=m;i++)
{
x=read();
y=read();
adde(x,y);
adde(y,x);
}
for(register int i=1;i<=n;i++)
bfs(i);
s1=read();
t1=read();
l1=read();
s2=read();
t2=read();
l2=read();
if(dis[s1][t1]>l1||dis[s2][t2]>l2)
{
printf("-1\n");
return 0;
}
ans=dis[s1][t1]+dis[s2][t2];
for(register int i=1;i<=n;i++)
{
for(register int j=1;j<=n;j++)
{
if(dis[s1][i]+dis[i][j]+dis[j][t1]<=l1&&dis[s2][i]+dis[i][j]+dis[j][t2]<=l2)
ans=min(ans,dis[s1][i]+dis[i][j]+dis[j][t1]+dis[s2][i]+dis[j][t2]);
if(dis[s1][j]+dis[i][j]+dis[i][t1]<=l1&&dis[s2][i]+dis[i][j]+dis[j][t2]<=l2)
ans=min(ans,dis[s1][j]+dis[i][j]+dis[i][t1]+dis[s2][i]+dis[j][t2]);
}
}
printf("%d\n",m-ans);
return 0;
}
原文:https://www.cnblogs.com/Peter0701/p/11519439.html