珠宝的故事 2018-06-02
传送门
一.二分+二分图染色
最小的最大,考虑二分。
每次二分影响值,将影响值不小于mid的两个人关进不同的监狱,即染成不同的颜色,分到二分图的两个不同的集合中。若最终形成的图是二分图,说明这种分法可行,影响值还有变小的可能;否则影响值只能更大。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=20000+100,M=100000+100; int n,m; struct node{ int to,nxt; long long w; }e[M<<1]; int head[N],tot; void add(int u,int v,long long w){ e[++tot]=(node){v,head[u],w}; head[u]=tot; } struct ed{ int u,v; long long w; }E[M]; int col[N]; bool dfs(int u,int c,long long mid){ col[u]=c; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(e[i].w>=mid){ if(col[v]==col[u]) return false; if(!col[v]&&!dfs(v,3-c,mid)) return false; } } return true; } bool check(long long mid){ for(int i=1;i<=n;i++) col[i]=0; //不必重新建图,只保留边权>mid的即可 for(int i=1;i<=n;i++) if(!col[i]) if(!dfs(i,1,mid)) return false; return true; } int main(){ scanf("%d%d",&n,&m); long long l=0,r=-(1<<30); for(int i=1;i<=m;i++){ int u,v; long long w; scanf("%d%d%lld",&u,&v,&w); int c=u; u=min(u,v);v=max(c,v); add(u,v,w);add(v,u,w); E[i]=(ed){u,v,w}; r=max(r,w); } r++;//扩大边界,防止漏解 while(l+1<r){ long long mid=(l+r)>>1; if(check(mid)) r=mid; else l=mid; } printf("%lld",l); return 0; }
二.并查集
贪心地想,将每对罪犯按影响值从大到小进行排序,每次将两个罪犯分到两个监狱中,直到出现冲突的情况,输出答案。
我们可以通过并查集来维护两个集合并查询集合所属关系。对于每个罪犯i,集合fa[i]表示在监狱1,集合fa[i+n]表示在监狱2。若出现两个罪犯在同一监狱的情况,说明出现了冲突,此时这对罪犯的影响值就是最小的影响值即为答案。最后注意特判没有冲突的情况。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=20000+100,M=100000+100; struct node{ int u,v; long long w; }e[M]; bool cmp(node a,node b){ return a.w>b.w; } int fa[N<<1]; int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int u,v; long long w; scanf("%d%d%lld",&u,&v,&w); e[i]=(node){u,v,w}; } sort(e+1,e+m+1,cmp); for(int i=1;i<=(n<<1);i++) fa[i]=i; for(int i=1;i<=m;i++){ int u=e[i].u,v=e[i].v; int fu=find(u),fv=find(v); if(fu==fv){ printf("%lld",e[i].w); return 0; } else { fa[fu]=find(v+n); fa[fv]=find(u+n); } } printf("0"); return 0; }<br />