题意:
给n个村庄的坐标和两个特殊点s1,s2的坐标,现在要将每个村庄连到s1或s2上,使n个村庄间的最大距离最小。
分析:
二分任意两村庄间的最大距离,用2-sat判断该最大距离是否可行,二分的时候可以顺便记录答案,不用等最后区间为空时再输出l或l-1。
代码:
//poj 2749 //sep9 #include <iostream> #include <vector> using namespace std; const int maxN=1024; const int maxL=4000000; vector<int> g[maxN],ng[maxN]; int n,m,cnt,scc,vis[maxN],dfn[maxN]; int x1,y1,x2,y2; int x[maxN],y[maxN]; int a,b; int ap[maxN],aq[maxN],bp[maxN],bq[maxN]; int d[maxN],D; void addegde(int u,int v) { g[u].push_back(v); ng[v].push_back(u); } void dfs(int k) { vis[k]=1; for(int i=g[k].size()-1;i>=0;--i) if(!vis[g[k][i]]) dfs(g[k][i]); dfn[++cnt]=k; } void ndfs(int k) { vis[k]=scc; for(int i=ng[k].size()-1;i>=0;--i) if(!vis[ng[k][i]]) ndfs(ng[k][i]); } void kosaraju() { memset(vis,0,sizeof(vis)); cnt=0; for(int i=1;i<=2*n;++i) if(!vis[i]) dfs(i); memset(vis,0,sizeof(vis)); scc=0; for(int i=2*n;i>=1;--i) if(!vis[dfn[i]]){ ++scc; ndfs(dfn[i]); } } int two_sat(int dis) { int i,j; for(i=1;i<=2*n;++i) g[i].clear(),ng[i].clear(); for(i=1;i<=a;++i){ int p=ap[i],q=aq[i]; addegde(p,q+n); addegde(p+n,q); addegde(q,p+n); addegde(q+n,p); } for(i=1;i<=b;++i){ int p=bp[i],q=bq[i]; addegde(p,q); addegde(q,p); addegde(p+n,q+n); addegde(q+n,p+n); } for(i=1;i<=n;++i) for(j=i+1;j<=n;++j) if(i!=j){ if(d[i]+d[j]>dis){ addegde(i,j+n); addegde(j,i+n); } if(d[i]+d[j+n]+D>dis){ addegde(j+n,i+n); addegde(i,j); } if(d[i+n]+d[j]+D>dis){ addegde(i+n,j+n); addegde(j,i); } if(d[i+n]+d[j+n]>dis){ addegde(i+n,j); addegde(j+n,i); } } kosaraju(); for(i=1;i<=n;++i) if(vis[i]==vis[i+n]) return 0; return 1; } int main() { int i,j; scanf("%d%d%d",&n,&a,&b); scanf("%d%d%d%d",&x1,&y1,&x2,&y2); for(i=1;i<=n;++i) scanf("%d%d",&x[i],&y[i]); for(i=1;i<=n;++i){ d[i]=abs(x[i]-x1)+abs(y[i]-y1); d[i+n]=abs(x[i]-x2)+abs(y[i]-y2); } D=abs(x1-x2)+abs(y1-y2); int p,q; for(i=1;i<=a;++i) scanf("%d%d",&ap[i],&aq[i]); for(i=1;i<=b;++i) scanf("%d%d",&bp[i],&bq[i]); int l=0,r=maxL,ans=-1; while(l<r){ int mid=(l+r)/2; if(two_sat(mid)==0) l=mid+1; else r=mid,ans=mid; } printf("%d",ans); return 0; }
时间: 2024-09-26 23:53:18