【牛客周赛 Round 35】 小红的无向图构造

文章讨论了如何在给定条件下构建连通图并保持节点间最短路径的特性。
摘要由CSDN通过智能技术生成

题目描述:小红希望你构造一个无向连通图,满足共有 n n n个点 m m m条边,且 i i i号节点到1号节点的最短路长度怡好为 a i a_i ai。你能帮帮她
吗?
输入描述:
第一行输入两个正整数 n , m n,m n,m, 代表构造的图的点数和边数。
第二行输入 n n n个整数 a i a_i ai,代表 i i i号节点到 1 号节点的最短路。 1 ≤ n , m ≤ 1 0 5 1\leq n,m\leq10^5 1n,m105

a 1 = 0 a_1=0 a1=0,对于 i ≥ 2 i\geq2 i2,1 ≤ a i ≤ n − 1 \leq a_i\leq n-1 ain1
输出描述:
如果无解,请输出 -1。
否则输出 m m m行,每行输出两个正整数 u , v u,v u,v,代表节点 u u u和节点 v v v有一条边连接。
请务必保证不含重边和自环,否则将直接判为答案错误。

示例一:

输入:

3 3
0 1 1

输出:

1 2
2 3
3 1

示例二:

输入:

3 3
0 1 2

输出:

-1

分析:要满足, i i i号节点到1号节点的最短路长度怡好为 a i a_i ai,则可以将 a i a_i ai看成每一层,这其中每一层可能会有多个节点,首先要求m>=n-1,此时才能连通,同时,可以先将每一层的0号节点连接起来,看看有没有多余的边,如果有多余的边,我们就要将其分配掉,怎么分配不会破坏最短路的性质呢,兄弟节点之间的相连和某一层节点到下一层节点相连不会,最后如果能连的边都连起来了边数依旧小于m,则输出-1
下面是完整代码

#include<iostream>
#include<queue>
#include<cstring>
#include<map>
#include<algorithm>
#include<vector>
#include<set>
#include<cmath>
#include<stack>
#include<random>
#include<ctime>
#define ll long long
#define ull unsigned long long 
#define pb push_back
#define fi first
#define se second
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define __int128_t int128
using namespace std;
typedef pair<int, int> PII;
typedef pair<ll, int> PLI;
int dx[4] = {-1,0,1,0},dy[4] = {0,1,0,-1};

const int N = 1e5 + 10;
int a[N];
vector<int> g[N];

int main() {
    ios
    int n, m;
    cin >> n >> m;
    int ma = 0;
    for (int i = 1; i <= m; i++) {
        cin >> a[i];
        ma = max(a[i], ma);
        g[a[i]].push_back(i);
    }
    if (m < n - 1) {
        cout << -1 << '\n';
        return 0;
    }
    vector<PII> v;
    for (int i = 1 ; i <= ma; i++) {
        if (g[i].empty()) {
            cout << -1 << '\n';
            return 0;
        }
        if(i > 1) {
            for (auto j : g[i]) {
                v.push_back({g[i - 1][0], j});
            }
        }
    }
    if (v.size() < m) {
        for (int i = 1; i <= ma; i++) {
            for (size_t j = 0; j < g[i].size(); j++) {
                for (size_t k = j + 1; k < g[i].size(); k++) {
                    v.push_back({g[i][j], g[i][k]}); // 连接兄弟节点
                    if (v.size() == m) break;
                }
                if (v.size() == m) break;
            }
            if (v.size() == m) break;
        }
        for (int i = 1; i <= ma; i++) {
            for (size_t j = 1; j < g[i].size(); j++) {
                if (i < ma) {
                    for (auto k : g[i + 1]) {
                        v.push_back({g[i][j], k}); // 连接下一层
                        if (v.size() == m) break;
                    }
                }
                if (v.size() == m) break;
            }
            if (v.size() == m) break;
        }
    }
    if (v.size() < m) {
        cout << -1 << '\n';
        return 0;
    }
    for (auto i : v) {
        cout << i.first << " " << i.second << '\n';
    }
    return 0;
}
  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值