zoukankan      html  css  js  c++  java
  • 网络流强化-HDU 3338-上下界限制最大流

      题意是:

      一种特殊的数独游戏,白色的方格给我们填1-9的数,有些带数字的黑色方格,右上角的数字代表从他开始往右一直到边界或者另外一个黑格子,中间经过的白格子的数字之和要等于这个数字;左下角的也是一样的意思,只是作用对象成了它下方的白格子。

      思路:

      既然所有行的数字之和等于所有列的数字之和,那么我们可以将行方向(向右)的点作为与源点连接的点,列方向(向下)的点作为与汇点连接的点。

      由于向右和向下的点可能在同一块方格里面,以及我们需要设置每个白格子的容量,所以我们需要拆点。

      题目要求填1-9的数,所以白格子起码都是1,我们不妨假设这些基础的1已经填好了,那么将对应行方向的点的入边(来自源点)容量减去该点作用范围内的白格子数目,将对应列方向的点的出边(去到汇点)容量减去该点作用范围内的白格子数目。然后设置白格子的容量为8就能保证填的都是1-9的数了。

      

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<cmath>
      4 using namespace std;
      5 #define maxe 100128  //pay  双向边 一共10万条路 双向就是20万 反边就是40万
      6 #define maxv 20128   //pay
      7 #define maxn 105    //pay
      8 #define sc scanf
      9 #define pt printf
     10 #define rep(i,a,b)  for(int i=(a);i<(b);++i)
     11 const int inf = 0x3f3f3f3f; 
     12 int cg,sp,ins;  //cg change sp是总流量 ins是加速回溯点
     13 int N,M,s,t,delta,UP_LIMIT;
     14 int q[maxv],fro,rea;
     15 typedef struct ed{
     16     int v,nxt,cap; //dis
     17 }ed;
     18 ed e[maxe];
     19 int tot,head[maxv],cur[maxv],vis[maxv],bk[maxv],d[maxv],num[maxv]; //
     20 int mi(int a,int b) {return a<b?a:b;}
     21 int mx(int a,int b) {return a>b?a:b;}
     22 void add(int u,int v,int cap)
     23 {
     24     e[tot].v=v;         e[tot].nxt=head[u];
     25     /*e[tot].dis=dis;*/     e[tot].cap=cap;
     26     head[u]=tot++;
     27 
     28     e[tot].v=u;         e[tot].nxt=head[v];
     29     /*e[tot].dis=-dis;*/    e[tot].cap=0;
     30     head[v]=tot++;
     31 } 
     32 // 仅有一次的BFS为ISAP节省了不少时间 
     33 bool bfs()
     34 {
     35     //数组模拟queue
     36     int u,v,i,my_all = t;
     37     for(i=0;i<=my_all;++i) vis[i]=num[i]=0;  //无须一定是my_all,涉及所有即可~~~~~~~~~~~~~~~~~~ 
     38     for(i=0;i<=my_all;++i) d[i]=UP_LIMIT;    //UP_LIMIT的设定是比最高的层次大1
     39     fro = rea = 0;
     40     q[rea] = t; ++rea;
     41     vis[t] = 1;
     42     d[t] = 0;
     43     ++num[d[t]];
     44     while (rea>fro) 
     45     {
     46         u = q[fro]; ++fro;
     47         //pt("BFSing~ : u=%d
    ",u);
     48         for (i=head[u]; i!=-1; i=e[i].nxt) 
     49         {
     50             v=e[i].v;
     51             if (!vis[v] && e[i^1].cap ) 
     52             {
     53                 vis[v] = true;
     54                 d[v] = d[u] + 1;
     55                 ++num[d[v]];
     56                 q[rea] = v; ++rea;
     57                 //pt("普度众生 u=%d,v=%d,d[u]=%d,d[v]=%d
    ",u,v,d[u],d[v]);
     58             }
     59         }
     60     }
     61     //int last_ed=-1;
     62     //把连接了到不了终点的点的边都去除 如果确定都能和终点连接 就不需要 
     63     /*for(u=0;u<=my_all;++u)
     64     {
     65         if(!vis[u]) continue;
     66         for (i=head[u]; i!=-1; i=e[i].nxt) 
     67         {
     68             v=e[i].v;
     69             if(!vis[v])
     70             {
     71                 if(i==head[u]){
     72                     head[u] = e[i].nxt;
     73                     continue;
     74                 }
     75                 else{
     76                     e[last_ed].nxt = e[i].nxt;
     77                     continue;
     78                 }
     79             }
     80             last_ed = i;
     81         }
     82     }*/
     83     return vis[s];
     84 }
     85 // 增广
     86 int augment()/*单源单汇 不用查这个*/
     87 {
     88     int  flow = inf, i;
     89     cg = t;
     90     // 从汇点到源点通过 p 追踪增广路径, flow 为一路上最小的残量
     91     while (cg != s) {
     92         i = bk[cg];
     93         if(flow>=e[i].cap)
     94         {
     95             flow = e[i].cap;
     96             ins = e[i^1].v;     
     97             //用来加速寻找,在最小流量断开的地方重新开始寻找
     98             //嗯,等一下 我这个是从终点往起点寻找,而确定增光路径是从起点到终点
     99             //那么起点是河流的上游,那么回溯的河段应该尽可能的往上游靠近
    100             //所以应该将flow>e[i].cap的大于号改成大于等于号
    101         }
    102         cg = e[i^1].v;
    103     }
    104     cg = t;
    105     // 从汇点到源点更新流量
    106     while (cg != s) {
    107         i = bk[cg];
    108         e[i].cap -= flow;
    109         e[i^1].cap += flow;
    110         cg = e[i^1].v;
    111     }
    112     return flow;
    113 }
    114 //由于每次修改层次的时候,都是在到剩下子节点的距离中挑选最短的加1 所以层次分明不会出现死循环 
    115 int max_flow()
    116 {
    117     int flow = 0,i,u,v;
    118     bool advanced;
    119     if(bfs()==false) return 0;
    120     //pt("零层妖塔=%d
    ",d[s]);
    121     u = s;
    122     memcpy(cur, head, sizeof(head));
    123     while (d[s] < UP_LIMIT)          //UP_LIMIT的设定是比最高的层次大1
    124     //终点是0,那么起点所在层次最多是N-1 同理,不是d[s]<t
    125     {
    126         if (u == t) 
    127         {
    128             flow += augment();
    129             u = ins;    //pay speed up
    130         }
    131         advanced = false;
    132         for (i = cur[u]; i!=-1; i=e[i].nxt) 
    133         { 
    134             v = e[i].v;
    135             //pt("SELECT: %d -> %d
    ",u,v); 
    136             if (e[i].cap && d[u] == d[v] + 1) 
    137             {
    138                 advanced = true;
    139                 bk[v] = i;
    140                 cur[u] = i;
    141               //  pt("%d -> %d ::d[u]=%d,d[v]=%d
    ",u,v,d[u],d[v]); 
    142                 u = v;
    143                 break;
    144             }
    145         }
    146         if (!advanced) 
    147         { // retreat
    148             int base = UP_LIMIT;        //UP_LIMIT的设定是比最高的层次大1
    149             for (i = head[u]; i != -1; i=e[i].nxt)
    150             {
    151                 if (e[i].cap&&base>d[e[i].v])
    152                 {
    153                     cur[u] = i;
    154                     base = d[e[i].v];
    155                 }
    156             }
    157             
    158             if (--num[d[u]] == 0)
    159             {
    160                 //pt("u=%d,d=%d
    ",u,d[u]);
    161                 //pt("BREAK FROM HERE
    ");
    162                 break; // gap 优化
    163             } 
    164             ++num[d[u] = base+1]; 
    165             //pt("------------------我来增加层次%d:%d
    ",m+1,u);
    166             //我以前一直在想 如果没有找到怎么办呢 现在发现原来找不到的话距离会被赋成base+1 
    167             //—— 比上界还高 所以接下来不会再访问到这个点 这个点也没有机会被减成0了——不会莫名其妙地break,哈哈哈
    168             if (u != s)
    169             {
    170                 //pt("from %d ",u);
    171                 u = e[bk[u]^1].v;
    172                 //pt("return to %d
    ",u);
    173             }
    174             else
    175             {
    176                 // pt("STILL AT S:%d
    ",s);
    177             }
    178         }
    179     }
    180     return flow;
    181 }
    182 
    183 void init()
    184 {
    185     tot=0;
    186     memset(head,-1,sizeof(head));   //pay 
    187 }
    188 char info[maxn][maxn][8];
    189 int  ok_ed[maxn][maxn];
    190 int is_black[maxn][maxn];
    191 int LEFT[maxn][maxn],RIGHT[maxn][maxn];
    192 //最大流部分没有什么问题了 关键在于终点源点、编号分配、题目理解建图上面
    193 //切记:不要建立无用的边!! 包括连接不到的点 和不和终点连通的点 
    194 //确保所有的点都要和终点连通——这样才是合法的点
    195 int main()
    196 {
    197     freopen("in.txt","r",stdin);
    198     
    199     /*数据初始化区*/
    200     s=0, bk[0]=-1;
    201 
    202     /*变量存放区*/
    203     int i,j,u,v,id,who,sub;
    204 
    205     while(~sc("%d%d",&N,&M))
    206     {
    207 
    208         /*数据初始化区*/
    209         init(); sp = 0;
    210         for(i=0;i<N;++i) for(j=0;j<M;++j) is_black[i][j] = 0;
    211         for(i=0;i<N;++i) for(j=0;j<M;++j) LEFT[i][j] = RIGHT[i][j] = ok_ed[i][j] = -1;
    212 
    213         /*数据读取区*/
    214         for(i=0;i<N;++i) for(j=0;j<M;++j) sc("%s",info[i][j]);
    215 
    216         /*关键数据赋值区*/
    217         delta  = (N)*(M); t=2*delta+1; s=2*delta; 
    218         UP_LIMIT = 2*delta + 2; 
    219                 //说明:这个是层次的无法达到的上界,所以一共有N个点的时候,
    220                 //如果从0开始编号,那么上界就是N;如果从1开始编号,上界就是N+1
    221 
    222         /*数据处理区*/ /*建图区*/
    223         for(i=0;i<N;++i) for(j=0;j<M;++j)
    224         {
    225             if(info[i][j][0]=='.')  continue; 
    226             is_black[i][j] = 1; 
    227             if(info[i][j][0]!='X')  LEFT[i][j] = (info[i][j][0]-'0')*100+(info[i][j][1]-'0')*10+info[i][j][2]-'0';
    228             if(info[i][j][4]!='X') RIGHT[i][j] = (info[i][j][4]-'0')*100+(info[i][j][5]-'0')*10+info[i][j][6]-'0';
    229         }
    230         for(i=0;i<N;++i) for(j=0;j<M;++j)
    231         {
    232             if(is_black[i][j]==0)  continue; 
    233             if(LEFT[i][j]==-1&&RIGHT[i][j]==-1) continue;
    234             
    235             if(LEFT[i][j]!=-1)
    236             {
    237                 id = i*M + j + delta; //往下的取小一点的编号
    238                 v=j; u=i+1; sub = 0;
    239                 while(u<N&&is_black[u][v]==0) ++sub,++u;
    240                 LEFT[i][j]-=sub;
    241                 add(id,t,LEFT[i][j]);
    242             }
    243             if(RIGHT[i][j]!=-1)
    244             {
    245                 id = i*M + j ; //往下的取小一点的编号
    246                 u=i; v=j+1;  sub=0;
    247                 while(v<M&&is_black[u][v]==0) ++sub,++v;
    248                 RIGHT[i][j]-=sub;
    249                 add(s,id,RIGHT[i][j]);
    250             }
    251         }
    252         for(i=0;i<N;++i) for(j=0;j<M;++j)
    253         {
    254             if(is_black[i][j])  continue;
    255             id = i*M +j;            //!!!居然把这个放在后面,那建立边就会失败了,相当于用这次的容量帮上次的建边 
    256             add(id,id+delta,8); ok_ed[i][j] = tot - 2; //value[i][j] - cap就是分配的流量 
    257             
    258             /*OUTPUT*/
    259             v=j; u=i-1;
    260             while(is_black[u][v]==0) --u;
    261             who = u*M + v;
    262             add(id+delta,who+delta,inf);
    263             /*INPUT*/
    264             u=i; v=j-1;
    265             while(is_black[u][v]==0) --v;
    266             who = u*M + v ;
    267             add(who,id,inf);
    268         }
    269         /*答案处理区*/
    270         sp = max_flow();
    271         //pt("sp=%d
    ",sp);
    272         for(i=0;i<N;++i) for(j=0;j<M;++j)
    273         {
    274             if(j>0) pt(" ");
    275             if(is_black[i][j]==1) pt("_");
    276             else
    277             {
    278                 who = ok_ed[i][j];
    279                 pt("%d", 9 - e[who].cap   ); 
    280                 
    281             } 
    282             if(j==M-1) pt("
    ");
    283         }
    284     }
    285     return 0;
    286 }
    287 
    288 /**
    289  * 友情链接:
    290  * http://www.renfei.org/blog/isap.html 解释ISAP原理
    291  * https://www.cnblogs.com/bosswnx/p/10353301.html 使用的数据结构和我的比较相近 
    292  */
    HDU 3338 ISAP

       2019年8月18日17:11:56

      想要知道这个增广大概是怎么回事,因为我之前觉得好像都是线性的。

      假设先走S->A1->B1->T,之后B1->T已经满流,此时再走S->A2->B1->T,走不通了。

      那么可以从B1往回增广走到A1,然后从A1开始增广寻找新的路径,如A1->B2->T。

      

      

      

  • 相关阅读:
    Android 隐式意图激活另外一个Actitity
    Android 显示意图激活另外一个Actitity
    Android 创建一个新的Activity
    调用meitu秀秀.so文件实现美图功能
    Android C代码回调java方法
    Android 在C代码中调用logcat
    Android java传递int类型数组给C
    Android java传递string类型数据给C
    使用pywin32处理excel文件
    利用 pywin32 操作 excel
  • 原文地址:https://www.cnblogs.com/lighten-up-belief/p/11372901.html
Copyright © 2011-2022 走看看