Codeforces 544D Destroying Roads【思维枚举+最短路】好题~好题!

探讨在一个给定的无向图中,如何通过优化路径重叠部分来减少使用边的数量,以达到最大化的边删除目标,同时确保指定路径间的距离不超过给定限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

D. Destroying Roads
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

In some country there are exactly n cities and m bidirectional roads connecting the cities. Cities are numbered with integers from 1 to n. If cities a and b are connected by a road, then in an hour you can go along this road either from city a to city b, or from city b to city a. The road network is such that from any city you can get to any other one by moving along the roads.

You want to destroy the largest possible number of roads in the country so that the remaining roads would allow you to get from city s1 to city t1 in at most l1 hours and get from city s2 to city t2 in at most l2 hours.

Determine what maximum number of roads you need to destroy in order to meet the condition of your plan. If it is impossible to reach the desired result, print -1.

Input

The first line contains two integers n, m (1 ≤ n ≤ 3000, ) — the number of cities and roads in the country, respectively.

Next m lines contain the descriptions of the roads as pairs of integers ai, bi (1 ≤ ai, bi ≤ n, ai ≠ bi). It is guaranteed that the roads that are given in the description can transport you from any city to any other one. It is guaranteed that each pair of cities has at most one road between them.

The last two lines contains three integers each, s1, t1, l1 and s2, t2, l2, respectively (1 ≤ si, ti ≤ n, 0 ≤ li ≤ n).

Output

Print a single number — the answer to the problem. If the it is impossible to meet the conditions, print -1.

Examples
Input
5 4
1 2
2 3
3 4
4 5
1 3 2
3 5 2
Output
0
Input
5 4
1 2
2 3
3 4
4 5
1 3 2
2 4 2
Output
1
Input
5 4
1 2
2 3
3 4
4 5
1 3 2
3 5 1
Output
-1

题目大意:


一个N个点,M条无向边的图。

让你确定最多可以去掉多少条边,使得从s1到t1的最短距离小于等于l1,并且使得从s2到t2的最短距离小于等于l2.


思路:


很显然,我们用的边越少,可以去掉的边就越多,问我们最多可以去掉多少条边,其实也就是再问我们最少用多少条边。

如果两条路没有相交重叠的部分,那么我们的最初答案肯定就是m-dist[s1][t1]-dist[s2][t2];


那么我们如何进行优化呢?使得使用的边尽可能的少呢?

答案很简单,我们希望两条路有重叠的部分。

又不难想到,对于两条路的重叠部分,其没有必要具有大于1段(交汇了之后如果再散开了就没有必要再汇在一起了,因为如果还有必要汇在一起的话就没有必要散开)。

再观察到数据范围N不是很大,所以我们可以O(n^2)来枚举重叠部分的起点和终点。

那么我们需要知道两点之间最短路,又因为每条边的长度都是1.那么其实SPFA是一个更好的选择(其实就变成了Bfs,O(n^2+m));


那么整理一下思路:

①我们SPFA预处理出两点间最短路。

②然后我们O(n^2)枚举重叠部分的起点和终点。

③然后判断两条路经过重叠部分是否都小于其各自的限制(l1,l2);

④维护答案。


这里注意一点:可能会存在重叠路径相反走向的情况,也就是说对于枚举出来的重叠部分两点u,v,对于两条路来讲,可能有一条从u-->v,另一条从v--->u.

所以枚举的时候再颠倒几次都判断一下即可。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
using namespace std;
vector<int >mp[3500];
int dist[3500][3500];
int vis[3500];
int n,m;
void SPFA(int ss,int d)
{
    for(int i=1;i<=n;i++)dist[d][i]=0x3f3f3f3f;
    dist[d][ss]=0;
    memset(vis,0,sizeof(vis));
    queue<int >s;
    s.push(ss);
    while(!s.empty())
    {
        int u=s.front();
        vis[u]=0;
        s.pop();
        for(int i=0;i<mp[u].size();i++)
        {
            int v=mp[u][i];
            if(dist[d][v]>dist[d][u]+1)
            {
                dist[d][v]=dist[d][u]+1;
                if(vis[v]==0)
                {
                    vis[v]=1;
                    s.push(v);
                }
            }
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=n;i++)mp[i].clear();
        for(int i=0;i<m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            mp[x].push_back(y);
            mp[y].push_back(x);
        }
        int s,t,len;
        int ss,tt,lenn;
        scanf("%d%d%d%d%d%d",&s,&t,&len,&ss,&tt,&lenn);
        for(int i=1;i<=n;i++)
        {
            SPFA(i,i);
        }
        if(dist[s][t]>len||dist[ss][tt]>lenn)
        {
            printf("-1\n");
        }
        else
        {
            int output=m-dist[s][t]-dist[ss][tt];
            if(output<0)output=0;
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    if(dist[s][i]+dist[i][j]+dist[j][t]<=len)
                    {
                        if(dist[ss][i]+dist[i][j]+dist[j][tt]<=lenn)
                        {
                            output=max(output,m-dist[i][j]-dist[s][i]-dist[ss][i]-dist[j][t]-dist[j][tt]);
                        }
                    }
                    if(dist[s][j]+dist[j][i]+dist[i][t]<=len)
                    {
                        if(dist[ss][i]+dist[i][j]+dist[j][tt]<=lenn)
                        {
                            output=max(output,m-dist[i][j]-dist[s][j]-dist[ss][i]-dist[i][t]-dist[j][tt]);
                        }
                    }
                    if(dist[s][i]+dist[i][j]+dist[j][t]<=len)
                    {
                        if(dist[ss][j]+dist[j][i]+dist[i][tt]<=lenn)
                        {
                            output=max(output,m-dist[i][j]-dist[s][i]-dist[ss][j]-dist[j][t]-dist[i][tt]);
                        }
                    }
                    if(dist[s][j]+dist[j][i]+dist[i][t]<=len)
                    {
                        if(dist[ss][j]+dist[j][i]+dist[i][tt]<=lenn)
                        {
                            output=max(output,m-dist[j][i]-dist[s][j]-dist[ss][j]-dist[i][t]-dist[i][tt]);
                        }
                    }
                }
            }
            printf("%d\n",output);
        }
    }
}





### Codeforces 思维思路和技巧 #### 预处理的重要性 对于许多竞赛编程问而言,预处理能够显著提高效率并简化后续操作。通过提前计算某些固定的数据结构或模式匹配表,可以在实际求解过程中节省大量时间。例如,在字符串处理类目中预先构建哈希表来加速查找过程[^1]。 #### 算法优化策略 针对特定类型的输入数据设计高效的解决方案至关重要。当面对大规模测试案例时,简单的暴力破解往往无法满足时限要求;此时则需考虑更高级别的算法改进措施,比如动态规划、贪心算法或是图论中的短路径算法等。此外,合理利用空间换取时间也是一种常见的优化手段[^2]。 #### STL库的应用价值 C++标准模板库提供了丰富的容器类型(vector, deque)、关联式容器(set,map)以及各种迭代器支持,极大地便利了程序开发工作。熟练掌握这些工具不仅有助于快速实现功能模块,还能有效减少代码量从而降低出错几率。特别是在涉及频繁插入删除场景下,优先选用双向队列deque而非单向链表list可获得更好的性能表现。 ```cpp #include <iostream> #include <deque> using namespace std; int main(){ deque<int> dq; // 向两端添加元素 dq.push_back(5); dq.push_front(3); cout << "Front element is: " << dq.front() << endl; cout << "Back element is : " << dq.back() << endl; return 0; } ``` #### 实际应用实例分析 以一道具体目为例:给定一系列查询指令,分别表示往左端/右端插入数值或者是询问某个指定位置到边界之间的小距离。此目的关键在于如何高效地追踪新状态而无需重复更新整个数组。采用双指针技术配合静态分配的一维数组即可轻松解决上述需求,同时保证O(n)级别的总运行成本[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值