D. Graph and Graph 【Codeforces Round 1002 (Div. 2)】

D. Graph and Graph

在这里插入图片描述

思路:

乍一看问题很棘手,仔细思考一下题目要求,发现最终答案小于无穷(即不是-1)的情况,只可能是在两张图都在 u , v u,v u,v 这两个相邻点上反复横跳,这样每一步操作的代价都为0。
可以先查找一下哪些点可以作为最终点:如果点 u u u在图1和图2中都有 v v v这个邻接点,那么 u u u v v v当然都可以标记为最终点。
现在需要判断图1和图2从给定的起始点出发,能否到达同一个最终点,并且要得到一个最小代价。很容易联想到最短路算法,但是这个问题有两张图,要如何操作呢?题目限制了每一步操作时两张图都要一起动,而且 n n n ∑ m \sum m m 的大小都限制在1000以内,不妨将图1在 u u u点,图2在 v v v点的这一状态 ( u , v ) (u,v) (uv) 视为一个点,每一次操作视为边,边权即为 ∣ u t − v t ∣ |u_t-v_t| utvt ,新点(状态)的数量不超过 n 2 n^2 n2,边的数量不超过 m 2 m^2 m2,此时在这张“状态图”上可以跑一遍Dijkstra,得到从源状态到达每一个状态的最小代价。最终状态一定满足 u = = v u==v u==v并且 u u u被标记为最终点 ,在所有最终状态里找到最小代价即为答案。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl '\n'
#define int long long
#define pb push_back
#define pii pair<int, int>
#define FU(i, a, b) for (int i = (a); i <= (b); ++i)
#define FD(i, a, b) for (int i = (a); i >= (b); --i)
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int N = 1005;

int n, s1, s2, m1, m2;
vector<int> e1[N],e2[N];
int dis[N][N],vis[N][N];

void dj(int s1, int s2) {
    memset(dis,INF,sizeof(dis));
    memset(vis,0,sizeof(vis));
    priority_queue<pair<int,pii>,vector<pair<int,pii>>,greater<pair<int,pii>>> qu;
    dis[s1][s2]=0;
    qu.push({0,{s1,s2}});
    while(!qu.empty()){
        int t1=qu.top().second.first,t2=qu.top().second.second;
        qu.pop();
        if(vis[t1][t2]==1)continue;
        vis[t1][t2]=1;
        for(int j1:e1[t1]){
            for(int j2:e2[t2]){
                if(dis[j1][j2]>dis[t1][t2]+abs(j1-j2)){
                    dis[j1][j2]=dis[t1][t2]+abs(j1-j2);
                    qu.push({dis[j1][j2],{j1,j2}});
                }
            }
        }
    }
}

void solve() {
    cin >> n >> s1 >> s2;
    FU(i, 1, n) {
        e1[i].clear();
        e2[i].clear();
    }
    // memset(bj,0,sizeof(bj));
    cin >> m1;
    FU(i, 1, m1) {
        int x,y;
        cin>>x>>y;
        e1[x].pb(y);
        e1[y].pb(x);
    }
    cin >> m2;
    FU(i, 1, m2) {
        int x,y;
        cin>>x>>y;
        e2[x].pb(y);
        e2[y].pb(x);
    }

    bool bj[N]={},noans=1;
    FU(i,1,n){
        for(int e:e1[i]){
            if(find(e2[i].begin(),e2[i].end(),e)!=e2[i].end()){
                bj[i]=1;
                noans=0;
                break;
            }
        }
    }
    if(noans){
        cout<<"-1\n";
        return;
    }
 
    // FU(i,1,n){
    //     if(bj[i])cout<<i<<" ";
    // }
    // cout<<endl;

    dj(s1,s2);

    // FU(i,1,n){
    //     FU(j,1,n){
    //         cout<<i<<" "<<j<<" = "<<dis[i][j]<<"; ";
    //     }
    //     cout<<endl;
    // }

    int ans = INF;
    FU(i,1,n){
        if(bj[i])
            ans = min(ans,dis[i][i]);
    }

    if(ans == INF){
        cout<<"-1\n";
    }else cout<<ans<<endl;
}

signed main() {
    cin.tie(0)->ios::sync_with_stdio(0);
    int T = 1;
    cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值