2018省选第一道网络流(好久没思考过网络流了

F.One-Way-Roads
题意是这样
有一个连通双向边的图,要求给每个边定一个方向,问如何可以使最大入度的那个点入度最小。
这样与混合欧拉回路有异曲同工之妙。

我的想法

要把每个点的入度限制,就相当与建立网络流1-n个点,每个点连向汇点,容量为d。
那么就相当于限制了最大入度为d。
然后对于每一条边建立一个新的结点,由源点指向其,然后其指向该边的两点,
意义为这个边只能连接一个点,且容量为1,然后二分d跑最大流判断最小d可以使得容量=边数,也就是每个边都确定了入度的方向即可。
这题补了一整天,与ar进行对拍,发现自己菜到怀疑板子的错误。(这里了解到了一个更快的网络流isap板子
这里写图片描述
源码:

#include<cstdio>
#include<cmath>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<iostream>
using namespace std;
const int maxn=55;
const int Ni = 1550;
 const int MAX = 1<<26;
 struct Edge{
     int u,v,c;
     int next;
 }edge[30*Ni];
 int n,m;
 int edn;//边数
 int p[Ni];//父亲
 int d[Ni];
 int sp,tp;//原点,汇点

 void addedge(int u,int v,int c)
 {
     edge[edn].u=u; edge[edn].v=v; edge[edn].c=c;
     edge[edn].next=p[u]; p[u]=edn++;

     edge[edn].u=v; edge[edn].v=u; edge[edn].c=0;
     edge[edn].next=p[v]; p[v]=edn++;
 }
 int bfs()
 {
     queue <int> q;
     memset(d,-1,sizeof(d));
     d[sp]=0;
     q.push(sp);
     while(!q.empty())
     {
         int cur=q.front();
         q.pop();
         for(int i=p[cur];i!=-1;i=edge[i].next)
         {
             int u=edge[i].v;
             if(d[u]==-1 && edge[i].c>0)
             {
                 d[u]=d[cur]+1;
                 q.push(u);
             }
         }
     }
     return d[tp] != -1;
 }
 int dfs(int a,int b)
 {
     int r=0;
     if(a==tp)return b;
     for(int i=p[a];i!=-1 && r<b;i=edge[i].next)
     {
         int u=edge[i].v;
         if(edge[i].c>0 && d[u]==d[a]+1)
         {
             int x=min(edge[i].c,b-r);
             x=dfs(u,x);
             r+=x;
             edge[i].c-=x;
             edge[i^1].c+=x;
         }
     }
     if(!r)d[a]=-2;
     return r;
 }

 int dinic(int sp,int tp)
 {
     int total=0,t;
     while(bfs())
     {
         while(t=dfs(sp,MAX))
         total+=t;
     }
     return total;
 }
struct ttt{
    int u,v;
};
ttt qq[100500];
int res(int x){
    edn=0;
    int i;
    memset(p,-1,sizeof(p));
    int t1=n;
    for(i=1;i<=m;i++){
        t1++;
        addedge(0,t1,1);
        addedge(t1,qq[i].u,1);
        addedge(t1,qq[i].v,1);
    }
    tp=++t1;
    for(i=1;i<=n;i++){
        addedge(i,tp,x);
    }
    int res=dinic(sp,tp);
    if(res==m)
    return 1;
    else
    return 0;
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out2.txt","w",stdout);
    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
    int T;
    int s,t;
    scanf("%d",&T);
    while(T--){
    scanf("%d",&n);
    scanf("%d",&m);
    sp=0;
    memset(p,-1,sizeof(p));
    for(i=1;i<=m;i++){
    scanf("%d %d",&qq[i].u,&qq[i].v);
    }
    int right1,left1,mid1;
    left1=0;right1=25050;
    t2=1e9;
    while(right1>=left1){
        mid1=(left1+right1)/2;
        t1=res(mid1);
        if(t1==1){
        t2=min(t2,mid1);
        right1=mid1-1;
        }else{
        left1=mid1+1;
        }
    }
    cout << t2 << endl;
    }
    return 0;
}

之前错误的想法(错误说明建图失败!!!

刚开始是想着建立拆点成2列建立,然后右边那一列连向汇点,且容量为d。
然后对于每一条边新建立一个结点,这个结点连向左边那列边的两头,意义为每个边只能进入到一个点,然后连向右边意义为入度,建两列的意义是因为有可能从A->B,也有可能B->A。
这样的话会出现如下错误
这里写图片描述
只看下面那部分 相当4-5与3-4这2条边 下面那个2含义为对于3-4这条边 3连向了4 上面那个1含义就成了4-5这条边 的入度连向了3~ 不符合要的那个想法 所以有错

然后下面是ar的源码,再次orz ar老师,日常吊打

#include <bits/stdc++.h>
using namespace std;
const int N = 6005;
const int inf = 0x3f3f3f3f;
struct edge{
    int from,to,cap,flow;
    edge(){}
    edge(int u,int v,int w){
        from=u,to=v,cap=w,flow=0;
    }
};
vector<int>G[N];
vector<edge>es;
int S,T,n,m;
int d[N],cur[N];
bool vis[N];
int add(int u,int v,int w){
    es.push_back(edge(u,v,w));
    es.push_back(edge(v,u,0));
    int ss=es.size();
    G[u].push_back(ss-2);
    G[v].push_back(ss-1);
    return ss-2;
}
bool bfs(){
    memset(vis,0,sizeof(vis));
    d[S]=0;vis[S]=1;
    queue<int>q;
    q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=0;i<G[x].size();++i){
            edge& e=es[G[x][i]];
            if(!vis[e.to]&&e.cap>e.flow){
                vis[e.to]=1;
                d[e.to]=d[x]+1;
                q.push(e.to);
            }
        }
    }
    return vis[T];
    }
int dfs(int x,int a){
    if(x==T||a==0)return a;
    int flow=0,f;
    for(int& i=cur[x];i<G[x].size();++i){
        edge& e=es[G[x][i]];
        if(d[x]+1==d[e.to]&&( f=dfs(e.to,min(a,e.cap-e.flow)))>0){!
            flow+=f;
            e.flow+=f;
            es[G[x][i]^1].flow-=f;
            a-=f;
            if(a==0)break;
        }

    }
    return flow;
}
int mf(){
    int flow=0;
    while(bfs()){
        memset(cur,0,sizeof(cur));
        flow+=dfs(S,inf);
    }
    return flow;
}

void clear(){
    for(int i=0;i<N;i++) G[i].clear();
    es.clear();
}
int idn[505][2];
int idm[2505][2];
int dd[505];
int clk;
vector<int> ans;
vector<pair<int,int> >Ms;
int TT;

void rebuild(int Md){
    ans.clear();
    clear();
    for(int i=1;i<=n;i++)
        add(S, idn[i][0], inf);
    for(int i=1;i<=n;i++)
        add(idn[i][1], T, min(Md,dd[i]));
    for(size_t i=0;i<Ms.size();i++){
        int xx = Ms[i].first;
        int yy = Ms[i].second;
        add(idn[xx][0],idm[i][0],1);
        add(idn[yy][0],idm[i][0],1);
        add(idm[i][1],idn[xx][1],1);
        add(idm[i][1],idn[yy][1],1);
        ans.push_back(add(idm[i][0], idm[i][1],1));
    }
}
int main(){
    freopen("in.txt","r",stdin);
   freopen("out1.txt","w",stdout);
    scanf("%d", &TT);
    S=0;T=1;
    int res=0;
    while(TT--){
       // cin >>res;
        scanf("%d%d",&n,&m);
       // cout << n <<"  " << m << endl;
        clk=2;
        clear();
        ans.clear();
        Ms.clear();
        memset(idn,0,sizeof idn);
        memset(idm, 0,sizeof idm);
        memset(d,0, sizeof d);
        memset(dd,0,sizeof dd);
        memset(cur,0,sizeof cur);
        for(int i=1;i<=n;i++) idn[i][0]=++clk, idn[i][1]=++clk;
        for(int i=0;i<m;i++){
            int xx,yy;
            scanf("%d%d", &xx,&yy);
            dd[xx]++;
            dd[yy]++;
            Ms.push_back(make_pair(xx, yy));
            idm[i][0]=++clk;
            idm[i][1]=++clk;
        }
        int maxd = *max_element(dd+1, dd+1+n);
        for(int ansd=0;ansd<=maxd;ansd++){
            rebuild(ansd);
            mf();
            assert(ans.size()==m);
            bool ok = 1;
            for(size_t i=0;i<ans.size();i++){
                ok &= es[ans[i]].flow;
            }
            if(ok){
                //cout <<res <<"is"<< t2  << endl;
                cout << ansd << endl;
                //printf("%dis%d\n",res,ansd);
                break;
            }
        }
    }
    return 0;
}

然后这里是对拍造数据的源码(渣渣苟活都活不下去了

#include <iostream>
#include <stdio.h>
#include <vector>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <map>
#include <set>
#include <queue>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long ll;
const ll Mod=1e9+7;
const ll maxn=1e5+7;
//int qq[maxn];
int pre[maxn];
void init(int n){
    for(int i=0;i<=n;i++)
    pre[i]=i;
}
int find1(int x){
    int r=x;
    while(r!=pre[r]){
        r=pre[r];
    }
    return r;
}

int find2(int x,int y){
    int x1=find1(x);
    int y1=find1(y);
    if(pre[x1]!=pre[y1]){
        pre[y1]=x1;
        return 0;
    }
    return 1;
}

int map1[505][505];
//vectot<int>q1;
int main(){
    freopen("in.txt","w",stdout);
    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,t5,n,m;
    srand((unsigned int)(time(NULL)));
    t1=1;
    t2=2;
    int T;
    T=10000;
    cout << T<< endl;
    while(T--){
    int max1=50;
    int max2=2500;
    //cout << T<< endl;
    //while(T--){
    n=((rand()%max1)*5)%max1+1;
    if(n<10)n+=10;
    init(n);
    m=((rand()%max1)*5)%max1+1;
    t3=1;
    cout << n<<  " " << m+n-1 << endl;
    for(i=2;i<=n;i++){
        t1=rand()%(i-1)+1;
        cout<< t1 <<" " <<i << endl;
    }
    while(m--){
    t1=((rand()%max1)*5)%n+1;
    t2=((rand()%max1)*5)%n+1;
    if(t1==t2){
    m++;continue;
    }
    cout <<t1 <<" " << t2<<endl;
    }
    }


    //cout << t1+t2 << "    " << t3 << endl;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值