HDU_4781_Assignment For Princess(构造)(13成都站现场A题)

题型:图论


题意:

n个点,m条边,边的长为“1、2、3……、m”,构造一个有向图,满足:

1、任意两点可以到达;

2、任意两点之间至多有一条边,且不存在自环;

3、任意一个环的权值都要是3的倍数。

如果没有这样的图,输出-1;


分析:

      为了满足条件1,可以先将点1到点n连成一条链,边的权值为“1、2、3……n-1”,然后将点n和点1连起来,若n%3==1,则权值为n+2,否则权值为n,这样现在连成的大环的权值就满足条件3了。

      接下来就是放剩下的边了。

      可以发现,若权值为len的边能够放在点u到点v,当且仅当dis[u→v]%3==len%3。

      对上诉命题证明:

        ∵   (dis[u→v]+dis[v→u])%3==0··············(即从1到n的大环的权值)

        ∴   (dis[u→v]%3+dis[v→u]%3)%3==0

        ∵   dis[u→v]%3==len%3

        ∴   (len%3+dis[v→u]%3)%3==0

        ∴   (len+dis[v→u])%3==0

        ∴   从u到v的新的小环的权值满足条件3

      为了在构造的时候满足条件2,可以用一个二维数组vis[u][v]来表示点u与点v之间是否有边。

如果构造不出来,则输出-1。


代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int n,m;
bool vis[1234];
int sum[100];
int map[100][100];
struct Node{
    int u,v,len;
}edge[1234];

bool fun(int len,int cnt){
    int tmp=len%3;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i!=j && !map[i][j] && !map[j][i]){
                if(i<j){
                    if((sum[j-1]-sum[i-1])%3==tmp){
                        edge[cnt].u=i;
                        edge[cnt].v=j;
                        edge[cnt].len=len;
                        map[i][j]=1;
                        return true;
                    }
                }
                else{
                    if((sum[i-1]-sum[j-1])%3==tmp){
                        edge[cnt].u=j;
                        edge[cnt].v=i;
                        edge[cnt].len=len;
                        map[j][i]=1;
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

int main() {
    int t,time=1;
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d",&n,&m);
        memset(map,0,sizeof(map));
        memset(sum,0,sizeof(sum));
        memset(vis,false,sizeof(vis));
        sum[0]=0;
        for(int i=1;i<n;i++){
            edge[i].u=i;
            edge[i].v=i+1;
            edge[i].len=i;
            vis[edge[i].len]=true;
            sum[i]=sum[i-1]+i;
            map[edge[i].u][edge[i].v]=1;
        }
        edge[n].u=n;
        edge[n].v=1;
        if(n%3==1) edge[n].len=n+2;
        else edge[n].len=n;
        vis[edge[n].len]=true;
        sum[n]=sum[n-1]+edge[n].len;
        map[edge[n].u][edge[n].v]=1;
/*
        cout<<"***************"<<endl;
        for(int i=1;i<=n;i++){
            cout<<sum[i]<<endl;
        }
        cout<<"***************"<<endl;
*/

        int cnt=n;
        bool flag=true;

        for(int i=1;i<=m;i++){
            if(!vis[i]){
                if(!fun(i,++cnt)){
                    flag = false;
                    break;
                }
            }
        }

        printf("Case #%d:\n",time++);
        if(!flag){
            printf("-1\n");
            continue;
        }

        for(int i=1;i<=m;i++){
            printf("%d %d %d\n",edge[i].u,edge[i].v,edge[i].len);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值