POJ 3228 网络流+二分&并查集
来源:互联网 发布:淘宝记录怎么删除 编辑:程序博客网 时间:2024/05/16 05:29
点击打开链接
题意:有n个城镇,第一行是金矿和金子数量,然后第二行是装金子的地方和能装的数量,在下面是m条道路,问你选择的道路中最大值最小,使得所有金子运到装金子的地方
思路:最大值最小,根本不用考虑一看就是二分,然后想了想就是个网络流的模型嘛,很简单,被坑了几次道路是双向的,改过之后A掉,然后看了看讨论还可以用并查集写,这里两种方法都写了,先是网络流的直接二分最大值,然后满足条件的边建模型,跑一边就行了
#include <queue>#include <vector>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <iostream>#include <algorithm>#include <functional>using namespace std;typedef long long ll;const int inf=0x3f3f3f3f;const int maxn=1010;struct edge{ int to,cap,rev; edge(int a,int b,int c){to=a;cap=b;rev=c;}};vector<edge>G[maxn];int level[maxn],iter[maxn];void add_edge(int from,int to,int cap){ G[from].push_back(edge(to,cap,G[to].size())); G[to].push_back(edge(from,0,G[from].size()-1));}void bfs(int s){ memset(level,-1,sizeof(level)); queue<int>que;level[s]=0; que.push(s); while(!que.empty()){ int v=que.front();que.pop(); for(unsigned int i=0;i<G[v].size();i++){ edge &e=G[v][i]; if(e.cap>0&&level[e.to]<0){ level[e.to]=level[v]+1; que.push(e.to); } } }}int dfs(int v,int t,int f){ if(v==t) return f; for(int &i=iter[v];i<G[v].size();i++){ edge &e=G[v][i]; if(e.cap>0&&level[v]<level[e.to]){ int d=dfs(e.to,t,min(f,e.cap)); if(d>0){ e.cap-=d; G[e.to][e.rev].cap+=d; return d; } } } return 0;}int max_flow(int s,int t){ int flow=0; while(1){ bfs(s); if(level[t]<0) return flow; memset(iter,0,sizeof(iter)); int f; while((f=dfs(s,t,inf))>0) flow+=f; }}int A[maxn*100],B[maxn*100],u[maxn*100],v[maxn*100],cost[maxn*100],n,m,sum;int judge(int mid){ for(int i=0;i<maxn;i++) G[i].clear(); for(int i=1;i<=n;i++) add_edge(0,i,A[i]); for(int i=1;i<=n;i++) add_edge(i,n+1,B[i]); for(int i=1;i<=m;i++){ if(cost[i]<=mid){ add_edge(u[i],v[i],inf); add_edge(v[i],u[i],inf); } } int ans=max_flow(0,n+1); if(ans==sum) return 1; else return 0;}int main(){ while(scanf("%d",&n)!=-1){ if(n==0) break; sum=0; for(int i=1;i<=n;i++){ scanf("%d",&A[i]); sum+=A[i]; } for(int i=1;i<=n;i++) scanf("%d",&B[i]); scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&u[i],&v[i],&cost[i]); int le=0,ri=10010; while(ri-le>1){ int mid=(le+ri)>>1; if(judge(mid)) ri=mid; else le=mid; } if(ri==10010) printf("No Solution\n"); else printf("%d\n",ri); } return 0;}第二种是并查集,根本没想到用并查集去写,看了discuss大家说这么写快很多,就写了一发,要的是最大的那个边最小,可以用贪心思想先排序,从小到大,满足条件之后目前加进去的边的最大一定是最小的,然后怎么判断呢,开两个权值数组即可,一个记录的是集合中金矿被开发的数量,另一个是集合中可用的装金子的数量,当集合中的金矿的金子的数量等于总的金矿金子数量并且装金子的数量>=总的金矿金子数量就满足,之前WA了几发是因为m可以等于0,这样如果
4
2 3 0 0
3 3 0 0
0
错的是输出 No Solution,但是应该输出0
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <iostream>#include <algorithm>using namespace std;typedef long long ll;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const ll INF=0x3f3f3f3f3f3f3f3fll;const int maxn=10010;int f[maxn],num_gol[maxn],num_cav[maxn],A[maxn],B[maxn],n,m,sum;struct edge{ int u,v,cost;}es[maxn*100];bool cmp(const edge &a,const edge &b){ return a.cost<b.cost;}int find1(int x){ if(x!=f[x]) f[x]=find1(f[x]); return f[x];}void unite(int a,int b){ int aa=find1(a); int bb=find1(b); if(aa==bb) return ; f[aa]=bb; num_gol[bb]+=num_gol[aa]; num_cav[bb]+=num_cav[aa];}int slove(){ int cnt=0,flag=0,ans=0; for(int i=0;i<m;i++){ if(find1(es[i].u!=find1(es[i].v))){ unite(es[i].u,es[i].v); int tt=find1(es[i].u); ans=max(ans,es[i].cost); if(num_gol[tt]==sum&&num_cav[tt]>=sum){ flag=1;break; } } if(flag) break; } if(flag) return ans; else return -1;}int main(){ while(scanf("%d",&n)!=-1){ if(n==0) break; sum=0; for(int i=0;i<=n;i++) f[i]=i; for(int i=1;i<=n;i++) scanf("%d",&A[i]); for(int i=1;i<=n;i++) scanf("%d",&B[i]); for(int i=1;i<=n;i++){ int tt=A[i]-B[i]; if(tt>=0){ num_gol[i]=A[i]-B[i]; num_cav[i]=0; sum+=num_gol[i]; }else{ num_gol[i]=0; num_cav[i]=B[i]-A[i]; } } scanf("%d",&m); for(int i=0;i<m;i++) scanf("%d%d%d",&es[i].u,&es[i].v,&es[i].cost); sort(es,es+m,cmp); if(sum==0){ printf("0\n");continue; } int ans=slove(); if(ans==-1) printf("No Solution\n"); else printf("%d\n",ans); } return 0;}
0 0
- POJ 3228 网络流+二分&并查集
- POJ 2253 Frogger(并查集+二分)
- hdu 3081 二分 + 并查集 + 网络流
- 【POJ】3228 Gold Transportation 二分+最大流or并查集
- POJ 2263 Heavy Cargo(二分+并查集)
- POJ-2985(树状数组 + 并查集 + 二分)
- POJ 1797 Heavy Transportation(二分+并查集/kruskal)
- poj 1330 Nearest Common Ancestors(并查集?!二分查找)
- POJ 2492 二分图判断 并查集
- POJ 3657 Haybale Guessing 并查集+二分答案
- Poj 3657 Haybale Guessing(二分+并查集)
- Poj 3657 Haybale Guessing(二分+并查集)
- hdu3081 二分+并查集+最大流
- HDU 3081Marriage Match II(二分+并查集+网络流之最大流)
- Marriage Match III (hdu 3277 网络流+并查集+二分)
- Poj 并查集
- poj并查集
- POJ 并查集
- C#中的访问修饰符
- C++实验6-项目4:字符删除
- 3611: [Heoi2014]大工程
- 2.1.2 为什么是uboot?
- 走进大数据Hadoop第一步hostname与hosts配置
- POJ 3228 网络流+二分&并查集
- 勺子难免碰锅沿,不咬舌怎么读θ和ð?
- 2.1.3 uboot必须解决哪些问题?
- Sublime Text 使用技巧
- ava包装类型的MAX_VALUE到底是多少啊
- Java之JVM虚拟机理解和实例
- 修改源码,自定义chromium启动页
- 2.1.4 uboot的工作方式
- hdu 1074 doing homework