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

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;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值