七天使的通讯

【问题描述】

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;
}
posted @ 2017-08-17 19:02  小蒟蒻ysn  阅读(247)  评论(0编辑  收藏  举报