[转载] $CF543B$ 题解

阅读原文

背景:\(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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值