七天使的通讯
【问题描述】
n个天使排成一条直线,某些天使之间需要互相联系,他们之间的通讯可以通过黑白两种通道中的一种;所有通道必须在直线同侧(另一侧是地面);为了保证通讯效率,同种颜色的所有通道之间不能相交。请计算能否建立这种通讯方案。
【输入】
第一行一个数T,表示接下来有T个询问。
对于每个询问:第一行两个数n,m,分别表示有n个天使、需要建立通讯线路的天使有m对;接下来有m行,每行两个数a、b,表示a、b两个天使需要通讯。
【输出】
对于每个询问,输出一行“sane”表示有可行方案、“non”表示无解。
【输入输出样例】
angelus.in
1
7 5
1 3
2 7
3 4
7 4
6 5
angelus.out
sane
【样例解释】
样例中共有一个询问。
在(1,3)、(4,7)、(5,6)之间连黑色通道,在(2,7)、(3,4)之间连白色通道,每条通道都成功建立,且同种颜色的通道没有相交,所以输出sane。
【数据规模和约定】
对于 20%的数据,1<=n<=50,1<=m<=15
对于 50%的数据,1<=n<=1000,1<=m<=300
对于 100%的数据,1<=n<=5000,1<=m<=1000,1<=T<=10,1<=a<=n,1<=b<=n
数据保证每对(a,b)不重复,且a不等于b
【提示】
当两条线路有一对相同的端点时,这两条线路不相交。
也就是说,对于线路(a,b)和线路(c,d)(a < b且c < d),当且仅当 a < c < b < d 或者 c < a < d < b 时这两条线路相交。
【题解】
先吐槽一下数据之水,我用这个贪心算法(错误解法)有80分。
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define fp(i,a,b) for(int i=a;i<=b;i++)
#define fq(i,a,b) for(int i=a;i>=b;i--)
#define il inline
#define ll long long
using namespace std;
int n,m,BL=0,BR=0,WR=0,WL=0;
bool flagw=0,flagb=0,flag=0;
struct line
{
int l,r;
}a[1005];
il int gi()
{
int x=0;
short int t=1;
char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') t=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*t;
}
bool cmp(line x,line y)
{
return (x.l<y.l)||(x.l==y.l&&x.r<y.r);
}
int main()
{
freopen("angelus.in","r",stdin);
freopen("angelus.out","w",stdout);
int t=gi();
fp(i,1,t)
{
n=gi();m=gi();
fp(i,1,m)
{
a[i].l=gi(),a[i].r=gi();
if(a[i].l>a[i].r) swap(a[i].l,a[i].r);
}
BL=0,BR=0;
flagw=1;flagb=1;flag=1;
sort(a+1,a+1+m,cmp);
WL=a[1].l,WR=a[1].r;
fp(i,2,m)
{
if((a[i].l<BL&&BL<a[i].r&&a[i].r<BR)||(BL<a[i].l&&a[i].l<BR&&BR<a[i].r)) flagb=0;
if((a[i].l<WL&&WL<a[i].r&&a[i].r<WR)||(WL<a[i].l&&a[i].l<WR&&WR<a[i].r)) flagw=0;
if(flagw) {WL=a[i].l;WR=a[i].r;continue;}
if(flagb) {BL=a[i].l;BR=a[i].r;continue;}
flag=0;printf("non\n");break;
}
if(flag) printf("sane\n");
}
fclose(stdin);
fclose(stdout);
return 0;
}
将每个通道设为一个节点,先暴力判断每两条通道如果是同种颜色会不会相交,如果会相交就在这两个节点之间连无向边,说明它们不能为同种颜色(必须在二分图两边)。然后对组成的无向图进行二分图判定(DFS染色),寻找其中是否有相邻两点颜色相同。如果染色成功(无上述情况)说明该图是一个二分图,即有解,否则无解。
另外,让我有点纠结的是为什么相邻两点颜色相同就不行。但想想也就明白了,一条通道为一个节点,如果两节点相邻即通道相交,不同颜色通道不能相交,解决问题。
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define fp(i,a,b) for(int i=a;i<=b;i++)
#define fq(i,a,b) for(int i=a;i>=b;i--)
#define il inline
#define ll long long
using namespace std;
int n,m,a[1005],b[1005],head[1005],cnt,ans,color[1005];
struct edge
{
int to,next;
}d[2000005];
il int gi()
{
int x=0;
short int t=1;
char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') t=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*t;
}
void add(int u,int v)
{
d[cnt]=(edge){v,head[u]};head[u]=cnt++;
d[cnt]=(edge){u,head[v]};head[v]=cnt++;
}
void dfs(int u,int fa)//判断是否有相邻两点颜色相同
{
if(!ans) return;
for(int e=head[u];e!=-1;e=d[e].next)
{
int v=d[e].to;
if(color[v]!=-1&&color[v]==color[u])//如果有相邻两点颜色相同,则无解
{
ans=0;
return;
}
if(color[v]!=-1) continue;//color标过色,说明这个点搜过,跳过
color[v]=!color[u];//尽量给相邻两点染不同颜色
dfs(v,u);
}
}
int main()
{
freopen("angelus.in","r",stdin);
freopen("angelus.out","w",stdout);
int t=gi();
while(t--)
{
n=gi();m=gi();
memset(color,-1,sizeof(color));
memset(head,-1,sizeof(head));
cnt=0;ans=1;
fp(i,1,m)
{
a[i]=gi(),b[i]=gi();
if(a[i]>b[i]) swap(a[i],b[i]);
for(int j=1;j<i;j++)
if(a[i]<a[j]&&a[j]<b[i]&&b[i]<b[j]) add(i,j);
else if(a[j]<a[i]&&a[i]<b[j]&&b[j]<b[i]) add(i,j);//如上题解思想
}
fp(i,1,m)
if(color[i]==-1) color[i]=0,dfs(i,0);//color为-1代表未标颜色,0,1代表两种颜色
ans ? printf("sane\n") : printf("non\n");
}
fclose(stdin);
fclose(stdout);
return 0;
}