HDU 4781 Assignment For Princess 构造

本文介绍了一种方法,用于构建一个包含指定数量顶点和边的强联通有向图,同时满足权值之和为3的倍数、路径权值之和的约束,以及边的唯一性和无自环等条件。通过构造环并添加额外边来确保所有条件被满足。
题意:

构造一个\(N(10 \leq N \leq 80)\)个顶点\(M(N+3 \leq M \leq \frac{N^2} {7})\)条边的有向图,要满足如下条件:

  • 每条边有一个\([1,M]\)之间的权值,而且每条边的权值都是独一无二的
  • 该有向图是强联通的,即任意两点都互相可达
  • 图没有自环,而且任意两点之间最多有一条边
  • 可以从任意一点出发,经过任意条边,一条边可以走多次,再回到出发点
  • 符合上述要求的路径权值之和为3的倍数
分析:

首先我们构造一个长度为\(N\)的环,而且环的权值之和为3的倍数。
构造过程如下:

在顶点\(i\)\(i+1\)之间连一条长度为\(i\)的有向边,然后在\(N \sim N+2\)之间选一个使得整个环权值和为3的倍数,把这个作为边\((N,1)\)的权值。

这样我们得到的图就满足题目中的要求,但是还剩下\(M-N\)条边没有处理。
对于还没有选用的权值\(w\),选择一对没有连边的顶点\((u,v)\)
\(u \rightarrow v\)的路径上的权值和为\(sum\),如果\(w \equiv sum \: (mod 3)\),那么就在这两点之间连边。
这样添边的过程,依然使得原图的性质满足题目要求。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int maxn = 80 + 10;
const int maxm = 1000;

int G[maxn][maxn], dis[maxn][maxn];

struct Edge
{
    int u, v, d;
    Edge() {}
    Edge(int u, int v, int d):u(u), v(v), d(d) {}
};

int n, m;
int ecnt;
Edge edges[maxm];
bool vis[maxm];

void AddEdge(int u, int v, int d) {
    edges[ecnt++] = Edge(u, v, d);
    G[u][v] = G[v][u] = true;
}

int main() {
    int T; scanf("%d", &T);
    for(int kase = 1; kase <= T; kase++) {
        scanf("%d%d", &n, &m);

        memset(vis, false, sizeof(vis));
        memset(G, false, sizeof(G));
        ecnt = 0;

        for(int i = 1; i < n; i++) {
            AddEdge(i, i + 1, i);
            vis[i] = true;
        }
        int sum = (n * (n - 1) / 2) % 3, tmp;
        for(int i = n; ; i++) if((i + sum) % 3 == 0) {
            vis[i] = true;
            AddEdge(n, 1, i);
            tmp = i;
            break;
        }

        for(int i = 2; i <= n; i++) {
            dis[i-1][i] = (i - 1) % 3;
            dis[i][i-1] = (3 - dis[i-1][i]) % 3;
            for(int j = i - 2; j >= 1; j--) {
                dis[j][i] = (dis[j][i-1] + dis[i-1][i]) % 3;
                dis[i][j] = (3 - dis[j][i]) % 3;
            }
        }
        dis[n][1] = tmp % 3;
        for(int i = 2; i < n; i++) dis[n][i] = (dis[n][1] + dis[1][i]) % 3;

        bool ok = true;
        for(int i = n; i <= m; i++) if(!vis[i]) {
            bool findit = false;
            for(int u = 1; u <= n; u++) {
                for(int v = 1; v <= n; v++) if(u != v) {
                    if(dis[u][v] % 3 == i % 3 && !G[u][v]) {
                        findit = true;
                        AddEdge(u, v, i);
                        break;
                    }
                }
                if(findit) break;
            }
            if(!findit) { ok = false; break; }
        }

        printf("Case #%d:\n", kase);
        if(ok) {
            for(int i = 0; i < m; i++)
                printf("%d %d %d\n", edges[i].u, edges[i].v, edges[i].d);
        }
        else printf("-1\n");
    }

    return 0;
}

转载于:https://www.cnblogs.com/AOQNRMGYXLMV/p/4911285.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值