题型:图论
题意:
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;
}