[ICPC2014 WF]Sensor Network

题意:有一些点,距离不超过\(d\)的连边。求最大团。
题解:
首先,最大团是NPC的。
求最大团,可以把边取反,变为最大独立集。
如果这个图是二分图,那我们就可以做了。
这个图是二分图,说明可以把能选择点分为两部分,每部分的最远点对距离都不超过\(d\)
考虑缩小可行集合使其满足二分图性质。
枚举答案的最远点对,那么,以这两个为圆心。距离为半径作圆,两个圆个公共部分是可行区域。
这个区域是“圆规”的形状,即“()”。
不难发现,在中间水平切一刀,上线两部分的最远点对都不超过\(d\)
这样就变成二分图了。
时间复杂度:\(O(n^2*m*\sqrt{n})\),即\(O(n^{4.5})\)
能过。

代码:

#include <stdio.h>
#define MN 210
#define MM 2010
#define inf 99999999
int X[102],Y[102];
bool ck(int x,int y,int d)
{
    return x*x+y*y<=d;
}
bool inc(int i,int j,int d)
{
    return ck(X[i]-X[j],Y[i]-Y[j],d);
}
bool bk[102][102];int dd[102],co[102];
void dfs0(int u,int c,int n)
{
    if(co[u])return;
    co[u]=c;
    for(int i=1;i<=n;i++)
    {
        if(bk[u][i])
            dfs0(i,3-c,n);
    }
}
int fr[MN],ne[MM],v[MM],w[MM],bs=0;bool bb[MN];
int dy[MN],dl[MN],od[MM],S,T,N,jl[MN];
void add(int a,int b,int c)
{
	v[bs]=b;
	w[bs]=od[bs]=c;
	ne[bs]=fr[a];
	fr[a]=bs++;
}
void addb(int a,int b,int c)
{
    add(a,b,c);
    add(b,a,0);
}
bool bfs()
{
    for(int i=1;i<=N;i++)
    {
		jl[i]=inf;
		bb[i]=false;
    }
    int he=0,ta=1;
    dl[0]=S;jl[S]=0;bb[S]=true;
    while(he<ta)
    {
        int u=dl[he];
        for(int i=fr[u];i!=-1;i=ne[i])
        {
            if(w[i]>0&&!bb[v[i]])
            {
                bb[v[i]]=true;
                jl[v[i]]=jl[u]+1;
                dl[ta++]=v[i];
            }
        }
        he+=1;
    }
    return jl[T]<inf;
}
int dfs(int u,int z)
{
    if(u==T)
        return z;
    for(int &i=dy[u];i!=-1;i=ne[i])
    {
        if(w[i]>0&&jl[v[i]]==jl[u]+1)
        {
            int t=dfs(v[i],z<w[i]?z:w[i]);
            if(t!=-1)
            {
                w[i]-=t;w[i^1]+=t;
                return t;
            }
        }
    }
    return -1;
}
int dinic()
{
    int jg=0;
    while(bfs())
    {
        for(int i=1;i<=N;i++)
			dy[i]=fr[i];
        while(1)
        {
            int rt=dfs(S,inf);
            if(rt==-1)
                break;
            jg+=rt;
        }
    }
    return jg;
}
int ans[102];
int main()
{
    int n,d,jg=1;
    scanf("%d%d",&n,&d);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&X[i],&Y[i]);
    for(int a=1;a<=n;a++)
    {
        for(int b=1;b<=n;b++)
        {
            if(a==b)continue;
            if(!inc(a,b,d*d))
                continue;
            int m=0,r=(X[a]-X[b])*(X[a]-X[b])+(Y[a]-Y[b])*(Y[a]-Y[b]);
            for(int i=1;i<=n;i++)
            {
                if(i==a||i==b)continue;
                if(inc(i,a,r)&&inc(i,b,r))
                    dd[++m]=i;
            }
            for(int i=1;i<=m;i++)co[i]=0;
            for(int i=1;i<=m;i++)
            {
                for(int j=1;j<=m;j++)
                    bk[i][j]=!inc(dd[i],dd[j],r);
            }
            for(int i=1;i<=m;i++)
            {
                if(co[i]==0)
                    dfs0(i,1,m);
            }
            N=m+2;S=N-1;T=N;bs=0;
            for(int i=1;i<=N;i++)fr[i]=-1;
            for(int i=1;i<=m;i++)
            {
                if(co[i]==1)
                {
                    addb(S,i,1);
                    for(int j=1;j<=m;j++)
                    {
                        if(bk[i][j])
                            addb(i,j,inf);
                    }
                }
                else addb(i,T,1);
            }
            int rt=m-dinic()+2;
            if(rt>jg)
            {
                int s=0;
                for(int i=1;i<=m;i++)
                {
                    if(co[i]==1&&jl[i]!=inf)
                        ans[s++]=dd[i];
                    else if(co[i]==2&&jl[i]==inf)
                        ans[s++]=dd[i];
                }
                ans[s++]=a;ans[s++]=b;
                jg=rt;
            }
        }
    }
    printf("%d\n",jg);
    if(jg==1)ans[0]=1;
    for(int i=0;i<jg;i++)
        printf("%d ",ans[i]);
    return 0;
}

此外,模拟退火也能过。

posted @ 2021-01-20 22:13  lnzwz  阅读(161)  评论(0编辑  收藏  举报