HIT Summer Training Day13(网络流费用流)

来源:互联网 发布:用友软件待遇怎么样 编辑:程序博客网 时间:2024/06/05 02:20

A-HOJ1646

  • 题意:N个点M条的无向图,求图的连通度(即删掉最少个点使图不连通)
  • 思路:枚举源点汇点,跑一边最小割。而题目要求删点而不是删边。故拆点,将I拆成I和I+n,I向I+n连容量为1的边。原图中的边连两条有向边。I向J+n,J向I+n,容量INF。每次割的最小值便是连通度。若要割掉N个点才不连通,则说明原图的连通度为N。
    #include<cstdio>    #include<cstring>    #include<algorithm>    #define inf 100000000    #define mm 3005    #define nn 300    using namespace std;    struct nod{int end,next,c;    }g[mm*5],G[mm*5];    char A[14];    int tot,i,tem,n,j,c,z,ans,S,F,D,T,a,b,w,m;    int dis[nn],cur[nn],fa[nn],q[nn*6];    void add(int a,int b,int c)     {g[tot]=(nod){b,fa[a],c};fa[a]=tot++;      g[tot]=(nod){a,fa[b],0};fa[b]=tot++;     }    bool bfs()     {int t,w,u,x;      memset(dis,-1,sizeof(dis));      dis[q[1]=S]=0;      for (t=0,w=1;t<w;){      x=q[++t];if (x==T)return 1;      for (int i=fa[x];i!=-1;i=g[i].next)      {u=g[i].end;       if (dis[u]==-1&&g[i].c)       {dis[u]=dis[x]+1;q[++w]=u;}      }}     return 0;     }    int dfs(int w,int tem)     {int c,u,ok=0;      if (w==T)return tem;      for (int i=fa[w];i!=-1;i=g[i].next){      u=g[i].end;      if (dis[u]==dis[w]+1&&g[i].c){      c=dfs(u,min(tem-ok,g[i].c));      g[i].c-=c;g[i^1].c+=c;      ok+=c;      if (ok==tem)return tem;      }      }      if (!ok)dis[w]=-1;      return ok;     }    int main()    {     while(scanf("%d%d",&n,&m)!=EOF){     if (n==0||n==1){     for (int i=1;i<=m;i++){         scanf("%s",A);         }      printf("%d\n",n);continue;     }     memset(fa,-1,sizeof(fa));tot=0;     for (int i=0;i<n;i++)add(i,i+n,1);     for (int i=1;i<=m;i++){         scanf(" (%d,%d)",&a,&b);         add(a+n,b,inf);         add(b+n,a,inf);         }     for (int i=0;i<tot;i++)G[i]=g[i];     tem=inf;     for (int i=0;i<n;i++)         for (int j=i+1;j<n;j++){     S=i+n;T=j;     ans=0;     while (bfs()){     ans+=dfs(S,inf);     }     for (int k=0;k<tot;k++)g[k].c=G[k].c;     tem=min(tem,ans);     }     printf("%d\n",(tem>=n)?n:tem);     }     return 0;    }

B-HDU1596

  • 题意:给你一个m*n的格子的棋盘,每个格子里面有一个非负数。从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
  • 思路:此题思路和下面H题思路相似。考虑网络流最小割。首先易想到根据棋盘奇偶性分离两个集合X,Y;建图:首先从S向X中的数连容量为其权值的边,其次从Y中的数向T连容量为其权值的边。然后若X和Y中的某个数相邻,则连一条为INF的边。为什么这么连呢?
    • 若跑完最小割,X中的I任于S相连,则表明选I这个数,那么若和I相邻的数
      J也选,而他们之间又有一条INF的边,所以程序不会去割他,但任然有增广路,则矛盾。
    • 若X中的I不与S相连,则表示不取这个数。则跑完整个图便是,不取一些数,和最小,且满足选的数不相邻。所以TOT-ans即为解。
    #include<cstdio>    #include<cstring>    #include<algorithm>    #define mm 150005    #define nn 5510    #define CH getchar()    using namespace std;    typedef long long ll;    const ll inf=100000000000ll;    struct nod{ll end,next,c;    }g[mm*2];    const ll han[4]={1,0,0,-1};    const ll lie[4]={0,1,-1,0};    ll tot,i,n,z,ci,ans,sum,S,T,a,b,w,m;    ll dis[nn],cur[nn],fa[nn],q[nn*5];    void add(ll a,ll b,ll c)    {g[tot]=(nod){b,fa[a],c};fa[a]=tot++;     g[tot]=(nod){a,fa[b],0};fa[b]=tot++;    }    bool bfs()     {ll t,w,i,u,x;      memset(dis,-1,sizeof(dis));      dis[q[1]=S]=0;      for (t=0,w=1;t<w;){      x=q[++t];      for (i=fa[x];i!=-1;i=g[i].next)      {u=g[i].end;       if (dis[u]==-1&&g[i].c)       {dis[u]=dis[x]+1;q[++w]=u;        if (u==T)return 1;       }      }}     return 0;     }    ll dfs(ll w,ll tem)     {ll c,i,u,ok=0;      if (w==T)return tem;      for (ll &i=cur[w];i!=-1;i=g[i].next){      u=g[i].end;      if (dis[u]==dis[w]+1&&g[i].c){      c=dfs(u,min(tem-ok,g[i].c));      g[i].c-=c;g[i^1].c+=c;      ok+=c;      if (ok==tem)return tem;      }      }      if (!ok)dis[w]=-1;      return ok;     }    void Dfs(ll w){         dis[w]=1;         for (ll i=fa[w];i!=-1;i=g[i].next){             ll u=g[i].end;             if (dis[u]==-1&&g[i].c){                ci++;Dfs(u);                }             }         }    ll id(ll a,ll b){        return (a-1)*m+b;        }    int main()    {     while(scanf("%lld%lld",&n,&m)!=EOF){     S=0;T=n*m+1;     memset(fa,-1,sizeof(fa));tot=0;sum=0;ans=0;     for (ll i=1;i<=n;i++)         for (ll j=1;j<=m;j++){             scanf("%lld",&a);sum+=a;             if ((i+j)%2==0)add(S,id(i,j),a);                  else add(id(i,j),T,a);                  }     ll x,y;     for (ll i=1;i<=n;i++)     for (ll j=1;j<=m;j++)if ((i+j)%2==0){         z=id(i,j);         for (ll k=0;k<4;k++){             x=i+han[k];y=j+lie[k];             if (x>0&&x<=n&&y>0&&y<=m){                     add(z,id(x,y),inf);                     }             }         }     while (bfs()){     for (ll i=S;i<=T;i++)cur[i]=fa[i];     ans+=dfs(S,inf);     }     printf("%lld\n",sum-ans);     }     return 0;    }

原创粉丝点击