[bzoj 4080] Wf2014 Sensor Network

27 篇文章 0 订阅
1 篇文章 0 订阅

will讲的题,网上几乎都是用随机化算法过得,代码短的出奇,为了维护标算的尊严,我决定推广一下表算。。。。

你敢信这题是二分图最大独立集!?

枚举距离不超过d的两个点,已这两个点为圆心,距离为半径分别作圆,会形成如下的图形


可以看到上半部分的点之间距离小于等于d,严格来说是小于确定两点之间的距离。下部分同理。

但是上下部分的点之间距离可能超过d,在这些点对之间连一条边。求最大独立集即为此两点确定的情况下最多可选的点数。

所有点对算出的答案取大值即可。

方案就用网络流,对残余网络搜索,确定两个点集和割边,确定最小点覆盖,取反后即为最大独立集。

不要忘了判断只能选一个点的情况,我wa了一下午。。。。。

代码巨长巨丑,但毕竟是官方解法。。。。

#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
const int INF = 1e9;
int flow[10005];
int vis[105],v[105],mat[105];
int node[10005],next[10005];
int a[105],c[105],cur[105];
int js[105],ex[105],q[105];
int n,d,ans,p1,p2,minx,N,tot,l,r;
bool flag;
vector <int> e[105], ANS;
struct Point
{
  int x,y;
  void read()
  {
    scanf("%d%d",&x,&y);
  }
  Point operator -(const Point &a)const
  { return (Point){x-a.x, y-a.y}; }
} dot[105];
 
int dis(Point P1,Point P2)
{
  return (P1.x-P2.x)*(P1.x-P2.x)+(P1.y-P2.y)*(P1.y-P2.y);
}
 
int mult(Point P1,Point P2)
{
  return P1.x*P2.y-P2.x*P1.y;
}
int mult(Point P0,Point P1,Point P2)
{
  return mult(P1-P0,P2-P0);
}
 
bool Judge(int x,int h){
  if (vis[x]==h) return 0;
  vis[x] = h;
  int len = e[x].size();
  for (int i=0;i<len;i++)
  if (mat[e[x][i]]==0 || Judge(mat[e[x][i]],h))
    {mat[e[x][i]] = x; return 1;}
  return 0;
}
 
void calc(int t1,int t2){
  int dd = dis(dot[t1],dot[t2]);
  int tmp = 0;
  for (int i=1;i<=n;i++)
  if (i!=t1 && i!=t2 && dis(dot[i],dot[t1])<=dd && dis(dot[i],dot[t2])<=dd)
  {
    if (mult(dot[t1],dot[t2],dot[i])>0) v[i] = 1;
      else v[i] = 2;
    tmp++;
  } else v[i] = 0;
 
  for (int i=1;i<=n;i++)
    mat[i] = 0, e[i].clear();
  for (int i=1;i<=n;i++) if (v[i]==1)
    for (int j=1;j<=n;j++) if (v[j]==2)
    if (dis(dot[i],dot[j])>d*d)
      e[i].push_back(j);
 
  for (int i=1;i<=n;i++)
    vis[i]=0;
  for (int i=1;i<=n;i++)
  if (v[i]==1 && Judge(i,i))
    tmp--;
  if (tmp+2>ans){
    ans = tmp+2;
    p1 = t1;
    p2 = t2;
  }
}
int cnt;
void add(int x,int y,int z){
  node[++tot]=y; next[tot]=a[x]; a[x]=tot; flow[tot]=z;
  node[++tot]=x; next[tot]=a[y]; a[y]=tot; flow[tot]=0;
}
 
void init(){
  memset(c,10,sizeof(c));
  memset(js,0,sizeof(js));
  for (q[l=r=1]=N,js[c[N]=0]=1;l<=r;l++)
    for (int i=a[q[l]];i;i=next[i])
    if ((i&1) && c[node[i]]>c[q[l]]+1)
      js[ c[ q[++r]=node[i] ] = c[q[l]]+1 ]++;
  for (int i=1;i<=N;i++) ex[i] = N-1, cur[i] = a[i];
}
 
void sap(int x){
  if (x==N){
    flag = 1; cnt-=minx;
    return;
  }
  int minn = minx, i;
  for (i=cur[x];i;i=next[i])
  if (flow[i]){
    if (c[x]==c[node[i]]+1){
      minx = min(minx, flow[i]);
      sap(node[i]);
      if (c[1]>=N) return;
      if (flag) break; else minx=minn;
    }
    ex[x] = min(ex[x], c[node[i]]);
  }
  cur[x] = i;
  if (flag){
    flow[i] -= minx; flow[i^1] += minx;
    cur[node[i]] = max(cur[node[i]], i^1);
  } else
  {
    if (--js[c[x]]==0) c[1] = N;
    js[ c[x]=ex[x]+1 ]++;
    cur[x] = a[x]; ex[x] = N-1;
    for (i=a[x];i;i=next[i])
    if (flow[i^1] && c[node[i]]==c[x]+1){
      cur[node[i]] = max(cur[node[i]], i^1);
      ex[node[i]] = min(ex[node[i]], c[x]);
    }
  }
}
 
void dfs(int x){
  if (vis[x]==1) return;
  vis[x] = 1;
  for (int i=a[x];i;i=next[i])
    if (flow[i]) dfs(node[i]);
}
 
void print(){
  int dd = dis(dot[p1],dot[p2]);
  for (int i=1;i<=n;i++)
  if (i!=p1 && i!=p2 && dis(dot[i],dot[p1])<=dd && dis(dot[i],dot[p2])<=dd)
  {
    if (mult(dot[p1],dot[p2],dot[i])>0) v[i] = 1;
      else v[i] = 2;
  } else v[i] = 0;
  N = n+2; tot = 1;
  for (int i=1;i<=n;i++)
  if (v[i]==1) add(1,i+1,1);
    else if (v[i]==2) add(i+1,N,1);
  for (int i=1;i<=n;i++) if (v[i]==1)
    for (int j=1;j<=n;j++) if (v[j]==2)
    if (dis(dot[i],dot[j])>d*d)
      add(i+1,j+1,INF);
 
  init();
  while (c[1]<N) {flag=0;minx=INF;sap(1);}
  for (int i=1;i<=N;i++)
    vis[i] = 0;
  dfs(1);
  ANS.push_back(p1); ANS.push_back(p2);
  for (int i=2;i<N;i++)
  if ((v[i-1]==1 && vis[i]==1) || (v[i-1]==2 && vis[i]==0))
    ANS.push_back(i-1);
  while (!ANS.empty()){
    printf("%d",ANS.back());
    ANS.pop_back();
    if (ANS.empty()) printf("\n");
      else printf(" ");
  }
}
 
int main(){
  scanf("%d%d",&n,&d);
  for (int i=1;i<=n;i++)
    dot[i].read();
  for (int i=1;i<=n;i++)
    for (int j=i+1;j<=n;j++)
    if (dis(dot[i],dot[j])<=d*d)
      calc(i,j);
  if (ans==0){
    printf("1\n1\n");
    return 0;
  }
  printf("%d\n",ans);
  print();
  return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值