POJ 3228 网络流+二分&并查集

点击打开链接

题意:有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;
}
时间: 2024-08-29 18:54:01

POJ 3228 网络流+二分&并查集的相关文章

poj 3228 Gold Transportation 并查集

题意: 有n和城市和m条路,每个城市都有产生金量和收集金量,现在要把所有黄金收集,求经过的最短边是多少. 分析: 二分+最大流或用并查集合并等价类. //poj 3228 //sep9 #include <iostream> #include <algorithm> using namespace std; const int maxN=256; const int maxM=10024; int p[maxN],sum[maxN]; struct Edge { int u,v,w

POJ 2263 Heavy Cargo(二分+并查集)

题目地址:POJ 2263 这题是在网上的一篇关于优先队列的博文中看到的..但是实在没看出跟优先队列有什么关系..我用的二分+并查集做出来了... 二分路的载重量.然后用并查集检查是否连通. 代码如下: #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <ctype.h> #

HDU 3081Marriage Match II(二分+并查集+网络流之最大流)

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=3081 有一段时间没写最大流的题了,这题建图居然想了好长时间...刚开始是按着最终的最大流即是做多轮数去想建图,结果根本没思路,后来想了想,可以用二分答案的思想来找最终答案.然后很明显的并查集,但是并查集学的略渣,居然卡在并查集上了..= =. 但是也不是并查集的事..是我建图的思想太正了,稍微用点逆向思维并查集就可以很好利用了. 建图思路是:建立一个源点与汇点,将女孩与源点相连,男孩与汇点相连,权值

POJ 1797 Heavy Transportation(二分+并查集/kruskal)

Heavy Transportation Time Limit: 3000MS   Memory Limit: 30000K Total Submissions: 24398   Accepted: 6472 Description Background Hugo Heavy is happy. After the breakdown of the Cargolifter project he can now expand business. But he needs a clever man

POJ 2524 Ubiquitous Religions (幷查集)

Ubiquitous Religions Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 23090   Accepted: 11378 Description There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in findi

[ACM] POJ 3295 Ubiquitous Religions (并查集)

Ubiquitous Religions Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 23093   Accepted: 11379 Description There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in findi

poj 1456 Supermarket (贪心+并查集)

# include <stdio.h> # include <algorithm> # include <string.h> using namespace std; int fa[10010]; struct node { int p; int d; }; struct node a[10010]; bool cmp(node a1,node a2)//利润从大到小 { return a1.p>a2.p; } int find(int x) { if(fa[x]

[ACM] POJ 1611 The Suspects (并查集,输出第i个人所在集合的总人数)

The Suspects Time Limit: 1000MS   Memory Limit: 20000K Total Submissions: 21586   Accepted: 10456 Description Severe acute respiratory syndrome (SARS), an atypical pneumonia of unknown aetiology, was recognized as a global threat in mid-March 2003. T

poj 2513 Colored Sticks 并查集 字典树 欧拉回路判断

点击打开链接题目链接 Colored Sticks Time Limit: 5000MS   Memory Limit: 128000K Total Submissions: 30273   Accepted: 8002 Description You are given a bunch of wooden sticks. Each endpoint of each stick is colored with some color. Is it possible to align the sti