L2-010. 排座位
关键字:用multimap实现并查集
布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。
输入格式:
输入第一行给出3个正整数:N(<= 100),即前来参宴的宾客总人数,则这些人从1到N编号;M为已知两两宾客之间的关系数;K为查询的条数。随后M行,每行给出一对宾客之间的关系,格式为:“宾客1 宾客2 关系”,其中“关系”为1表示是朋友,-1表示是死对头。注意两个人不可能既是朋友又是敌人。最后K行,每行给出一对需要查询的宾客编号。
这里假设朋友的朋友也是朋友。但敌人的敌人并不一定就是朋友,朋友的敌人也不一定是敌人。只有单纯直接的敌对关系才是绝对不能同席的。
输出格式:
对每个查询输出一行结果:如果两位宾客之间是朋友,且没有敌对关系,则输出“No problem”;如果他们之间并不是朋友,但也不敌对,则输出“OK”;如果他们之间有敌对,然而也有共同的朋友,则输出“OK but…”;如果他们之间只有敌对关系,则输出“No way”。
输入样例:
7 8 4
5 6 1
2 7 -1
1 3 1
3 4 1
6 7 -1
1 2 1
1 4 1
2 3 -1
3 4
5 7
2 3
7 2
输出样例:
No problem
OK
OK but…
No way
C语言 标准的并查集算法
AC解答(不是我自己写的)
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<string.h>
using namespace std;
int w[105][105],f[105];
int fine(int v)
{
if(f[v]==v) return v;
return f[v]=fine(f[v]);
}
void mage(int v,int w)
{
int t1=fine(v);
int t2=fine(w);
if(t1!=t2)
f[t1]=t2;
}
int main()
{
int n,m,k,i,a,b,c;
scanf("%d%d%d",&n,&m,&k);
for(i=0; i<n; i++)
{
f[i]=i;
}
for(i=0; i<m; i++)
{
scanf("%d%d%d",&a,&b,&c);
if(c==1)
mage(a,b);
w[a][b]=c;
w[b][a]=c;
}
for(i=0; i<k; i++)
{
scanf("%d%d",&a,&b);
if(fine(a)==fine(b)&&w[a][b]!=-1) printf("No problem\n");
else if(fine(a)!=fine(b)&&w[a][b]!=-1) printf("OK\n");
else if(fine(a)==fine(b)&&w[a][b]==-1) printf("OK but...\n");
else printf("No way\n");
}
}
22/25代码
先看代码
/*
Name: L2-010. 排座位
Author: spencercjh
Date: 2017年10月22日 12:11:23
Description:团体程序设计天梯赛-练习集 GPLT
https://www.patest.cn/contests/gplt/L2-010
ref:http://blog.csdn.net/lhrsdl/article/details/38119577
http://www.cplusplus.com/reference/map/multimap/find/
http://www.cnblogs.com/dongsheng/archive/2013/09/10/3311594.html
http://www.cplusplus.com/reference/map/multimap/equal_range/
*/
#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<numeric>
#include<stack>
using namespace::std;
int main()
{
int N,M,K;
scanf("%d %d %d",&N,&M,&K);
multimap<int,int> Friend;
multimap<int,int> Enemy;
for(int i=1; i<=N; i++) //初始化mutimap
{
Friend.insert(pair<int,int>(i,i));
Enemy.insert(pair<int,int>(i,i));
}
auto j=Friend.begin(),k=Friend.begin(),p=Enemy.begin(),q=Enemy.begin();
for(int i=0; i<M; i++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
if(c==1)
{
j=Friend.find(a);
k=Friend.find(b);
if(j->first!=k->first)
{
Friend.insert(pair<int,int>(a,b));
Friend.insert(pair<int,int>(b,a));
}
}
else if(c==-1)
{
p=Enemy.find(a);
q=Enemy.find(b);
if(p->first!=q->first)
{
Enemy.insert(pair<int,int>(a,b));
Enemy.insert(pair<int,int>(b,a));
}
}
}
for(int i=0; i<K; i++)
{
int a,b;
scanf("%d %d",&a,&b);
bool friendflag=false,enemyflag=false,commonfriend=false;
auto Friendbeg=Friend.lower_bound(a),Friendend=Friend.upper_bound(a);
while(Friendbeg!=Friendend)
{
if(Friendbeg->second==b)
{
friendflag=true;
break;
}
Friendbeg++;
}
auto Enemybeg=Enemy.lower_bound(a),Enemyend=Enemy.upper_bound(a);
while(Enemybeg!=Enemyend)
{
if(Enemybeg->second==b)
{
enemyflag=true;
break;
}
Enemybeg++;
}
auto commonbeg=Friend.lower_bound(b),commonend=Friend.upper_bound(b);
if(enemyflag) //双方有敌对关系,寻找双方的共同朋友
{
Friendbeg=Friend.lower_bound(a); //之前Friendbeg已经经历过一个循环,在此规整
while(Friendbeg!=Friendend)
{
commonbeg=Friend.lower_bound(b); //之前commonbeg已经经历过一个循环,在此规整
while(commonbeg!=commonend)
{
if(Friendbeg->second==commonbeg->second)
{
commonfriend=true;
break;
}
commonbeg++;
}
Friendbeg++;
}
}
if(friendflag)
printf("No problem\n");
else if(enemyflag)
{
if(commonfriend)
printf("OK but...\n");
else
printf("No way\n");
}
else
printf("OK\n");
}
}
mutimap在本题中的使用
mutimap:http://www.cplusplus.com/reference/map/multimap/
关系比较复杂,需要2个mutimap——Friend,Enemy。
数据存储——
mutimap(key,value).将2人关系存入mutimap中,标识符1加入Friend,标识符为-1加入Enemy。你会发现我插入的时候很重复,(a,b)和(b,a)都要插进去,首先是因为如果只插入(a,b),查询(b,a)的关系的时候就会出问题,其次后面寻找敌人的共同朋友,也需要b作为key去查他的朋友。(但是讲道理,并查集作为树和森林的应用,这样操作打乱了结点之间应该有的正确逻辑关系。)
.............
Friend.insert(pair<int,int>(a,b));
Friend.insert(pair<int,int>(b,a));
..............
开始查询——
这里就涉及到mutimap的遍历:http://www.cnblogs.com/dongsheng/archive/2013/09/10/3311594.html
这里我使用的是链接中介绍的第二种,用lower_bound和upper_bound找到输入key(a)的value的上下界,遍历key的所有value,在其中查找第二个人(b)。
auto Friendbeg=Friend.lower_bound(a),Friendend=Friend.upper_bound(a);
while(Friendbeg!=Friendend)
{
if(Friendbeg->second==b)
{
friendflag=true;
break;
}
Friendbeg++;
}
Enemy同理。
题目又多了一个要求,即还要考察敌人之间的共同朋友,这也是我最为担心的部分。这里会有一个嵌套循环,我一直怀疑为超时,幸好没有:)。外层循环将a作为key遍历value,内层循环将b作为key遍历value,这期间找到一个相同的值就说明这2个敌人有共同敌人。
auto commonbeg=Friend.lower_bound(b),commonend=Friend.upper_bound(b);
if(enemyflag) //双方有敌对关系,寻找双方的共同朋友
{
Friendbeg=Friend.lower_bound(a); //之前Friendbeg已经经历过一个循环,在此规整
while(Friendbeg!=Friendend)
{
commonbeg=Friend.lower_bound(b); //之前commonbeg已经经历过一个循环,在此规整
while(commonbeg!=commonend)
{
if(Friendbeg->second==commonbeg->second)
{
commonfriend=true;
break;
}
commonbeg++;
}
Friendbeg++;
}
}
最后按照题目要求的逻辑输出对应语句
if(friendflag)
printf("No problem\n");
else if(enemyflag)
{
if(commonfriend)
printf("OK but...\n");
else
printf("No way\n");
}
else
printf("OK\n");
}
总结
使用stl_mutimap编程,虽然代码行数比C语言标准并查集算法多了一倍,但泛型编程风格代码容易读容易写容易懂,我更喜欢这样的编码方式。