hdu4975--dinic算法(1)

好像还能用isap和矩阵dp的,不过我没咋闹明白isap,分层能看懂后面就完全看不懂了,等今天晚上看懂了在写一个用isap做这道题的方法。嗯,立flag;
题意:给出 行数m和列数n,和各行各列的和,求这个矩阵可能的排列,不同的结果不同的输出,我都写到注释上面了。
方法:dinic,就是ek算法的增强版,先以源点为0,然后对整个网络进行分层,一层一层的分,分完层在进行dfs,跑一个while,不断的对残余网络分层,搜索,知道分层分不了了,也就是说到不了终点了就完事。
思路是不难理解的,见图的思路很值得学习,
注意点1 有一个弧优化,就是在进行dfs的时候,这样dfs就可以用while了,试想dfs的对树的搜索,如果用这个方法,就可以在达到目的之前对 左,中,右进行遍历。不然只会返回一个结果,
2 在对 残残残残残余网络之后见图 在判断 路径的大小,这样就会很省时间

#include <queue>
#include <vector>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#include <functional>
const int maxn=2000;
using namespace std;
typedef long long ll;
struct edge{
int to,cap,rev;
edge(int a,int b,int c){to=a,cap=b,rev=c;}
//分别是目的地,记录反向边的坐标,和容量;
};
vector<edge>G[maxn];
vector<int>G1[maxn];
bool vis[maxn];
int cengci[maxn];
int n,m;
int itr[maxn];
void addedge(int from,int to,int value)
{   G[from].push_back((edge){to,value,G[to].size()});
    G[to].push_back((edge){from,0,G[from].size()-1});

}
void add_edge(int from,int to)
{   G1[from].push_back(to);

}
void bfs(int s)
{    queue<int>q;
     q.push(s);
     memset(cengci,-1,sizeof(cengci));
     cengci[s]=0;//这和上面一行都是分层必要的初始化;
     while(!q.empty())
     {   int u=q.front();
     q.pop();
        for(int i=0;i<G[u].size();i++)
         {   edge &s=G[u][i];
             if(cengci[s.to]<0&&s.cap>0)
               {cengci[s.to]=cengci[u]+1;//分层;
               q.push(s.to);}



         }

     }


}
int dfs(int s,int t,int f)
{   //int flow;
   if(s==t) return f;//dfs结束的条件

   for(int &i=itr[s];i<G[s].size();i++)
   {  edge &w=G[s][i];
       if(w.cap>0&&cengci[w.to]>cengci[s])
       {  int d=dfs(w.to,t,min(w.cap,f));
            if(d>0)
            {   w.cap-=d;
                G[w.to][w.rev].cap+=d;//反向边,自己画一个二维数组就理解了。g就是二位数组。
                return d;

            }
       }



   }


return 0;
}
int max_flow(int s,int t)
{  int INF=0x3f3f3f3f;
int flow=0;
int cas=1;
   while(1)
   {   bfs(s);
     memset(itr,0,sizeof(itr));
      if(cengci[t]<0)
        {  //printf("%d!!! %d\n",t,cengci[t]);
            //printf("%d!!!!\n",cas++);
            return flow;}
      int l;//=dfs(s,t,INF);
       while(l=dfs(s,t,INF))//多次进行dfs,而不是一次分层然后一次dfs,
         flow+=l;

   }


}
bool judge_dfs(int x,int val)
{   vis[x]=1;
    for(int i=0;i<G1[x].size();i++)
    {   int t=G1[x][i];
        if(t==val) continue;//不找反向边。反向边一定能成为环所以你在逗我??
        if(vis[t]) return 1;//确定已经找到环了,因为vis被预定就是说明是环了
        if(judge_dfs(t,x)) return 1;//继续dfs,把后继节点的t变成左边的x,这样在下一个dfs中如果t==val就是说明
        //他的后继节点是前驱结点,所以就是环咯。

    }
    vis[x]=0;
    return 0;
}
bool judge()
{   memset(vis,0,sizeof(vis));
   for(int i=0;i<=m+n+1;i++)
   {   for(int j=0;j<G[i].size();j++)
         {edge &e=G[i][j];
         if(e.cap>0)//你看你看,如果在残余网络中边还存在,那么就见图,新建的图已经找不到一条增广路径了555;
          add_edge(i,e.to);}


   }
    for(int i=0;i<=n;i++)
       if(judge_dfs(i,-1)) return 1;//第二个参数设定成多少都行,只要不在 点的范围里。。就是钦定的边,就是说,如果判断某个具体的点能不能
       //构成环。

    return 0;
}
int main(){
    int T;
    int a,b,t=1;
    scanf("%d",&T);
    while(T--)
    {  scanf("%d%d",&n,&m);
       for(int i=0;i<maxn;i++)
       {  G[i].clear();
          G1[i].clear();
       }
       int sum1=0,sum2=0,sum=0;
        for(int i=1;i<=n;i++)
          {scanf("%d",&a);
         addedge(0,i,a);
     sum+=a,sum1+=a;//把所有杭上的点给加了
     //也就是左边的原点到左边一排的最大流。
         }
         for(int j=n+1;j<=m+n;j++)
         {   scanf("%d",&a);
            addedge(j,m+n+1,a);
            sum2+=a;//右边的最大流;
         }
         if(sum1!=sum2)
         {printf("Case #%d: So naive!\n",t++);//不能成立,数据有问题啊哈哈
          continue;
         }
          for(int i=1;i<=n;i++)
            for(int j=1+n;j<=m+n;j++)
          {  addedge(i,j,9);
          }
         int ans=max_flow(0,m+n+1);
          //printf("%d   %d*******\n",ans,sum);
        if(ans!=sum)
            printf("Case #%d: So naive!\n",t++);//最大流跑不到和,不能成立。
        else
        {     if(judge())
             printf("Case #%d: So young!\n",t++);//好多种类方法。
             //在这个图中
             //这个图表示的意思 就是各个行和和列和相互制约关系的成立。
             //而这个图中有环,就说明了把每个边权加一个少一个都是没关系的。这部就有多种情况了嘛。
             else
                printf("Case #%d: So simple!\n",t++);//只有一种情况,费了好大劲做出来。-。-好简单啊。


        }



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值