网络流强化-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\n",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\n",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\n",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\n",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\n",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\n",u,d[u]);
161                 //pt("BREAK FROM HERE\n");
162                 break; // gap 优化
163             }
164             ++num[d[u] = base+1];
165             //pt("------------------我来增加层次%d:%d\n",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\n",u);
173             }
174             else
175             {
176                 // pt("STILL AT S:%d\n",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\n",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("\n");
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。

  

  

  

原文地址:https://www.cnblogs.com/lighten-up-belief/p/11372901.html

时间: 2024-10-08 06:51:11

网络流强化-HDU 3338-上下界限制最大流的相关文章

BZOJ 2055 80人环游世界 有上下界的费用流

题目大意:给定n个点,每个点有固定的经过次数,m个人从任意节点出发任意节点结束,只能向右走,要求总边权和最小 有源汇.有上下界的费用流 其实上下界费用流有两种写法- - 一种是按照上下界网络流那么转化- - 一种是把必经边的费用减掉一个INF 跑完再加回去 我比较倾向于第一种写法- - 第二种写法在INF的取值上有点麻烦- - #include <cstdio> #include <cstring> #include <iostream> #include <al

ZOJ 3229 Shoot the Bullet(有源汇有上下界的最大流)

ZOJ 3229 Shoot the Bullet 链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3229 题意:一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝最多给C个女神拍照,每天拍照数不能超过D张,而且给每个女神 i 拍照有数量限制[Li,Ri],对于每个女神n天的拍照总和不能超过Gi,如果有解求屌丝最多能拍多少张照,并求每天给对应女神拍多少张照:否则输出-1. 思路: 有源汇有上下界的最大流 1. 在原先

zoj 3229 有源汇有上下界的最大流模板题

/*坑啊,pe的程序在zoj上原来是wa. 题目大意:一个屌丝给m个女神拍照.计划拍照n天,每一天屌丝最多个C个女神拍照,每天拍照数不能超过D张,并且给每一个女神i拍照有数量限制[Li,Ri], 对于每一个女神n天的拍照总和不能超过Gi,假设有解求屌丝最多能拍多少张照,并求每天给相应女神拍多少张照:否则输出-1. 解题思路:增设一源点st,汇点sd.st到第i天连一条上界为Di下界为0的边,每一个女神到汇点连一条下界为Gi上界为oo的边,对于每一天,当天到第i个女孩连一条[Li.Ri]的边. 建

有上下界的最大流解法

问题模型: 给定一个加权的有向图,满足: (1)容量限制条件: (2)流量平衡条件: (2)中的即除了源汇外,所有点都满足流量平衡条件,则称G为有源汇网络:否则,即不存在源汇,所有点都满足流量平衡条件,则称G为无源汇网络. 将这类问题由易到难一一解决: 问题[1] 求无源汇的网络有上下界的可行流 由于下界是一条弧上的流必需要满足的确定值.下面引入必要弧的概念:必要弧是一定流要满的弧.必要弧的构造,将容量下界的限制分离开了,从而构造了一个没有下界的网络G': 1. 将原弧(u,v)分离出一条必要弧

POJ 2396 Budget (有源汇有上下界的可行流)

POJ 2396 Budget 链接:http://poj.org/problem?id=2396 题意:给定一个M*N的矩阵,给定每行每列的和,以及其中一些值的限定条件,问能否构成一个可行的矩阵. 思路: 添加一个源点,向每行连边,每条边的上下界都为该行的和:添加一个汇点,每列向汇点连边,边的上下界都为该列的和.然后每行向每列连边,边的上下界一开始为(0,INF),之后通过一些限定条件更新. 现在问题成了求一个有源汇有上下界的可行流.只需要再添加一个超级源点,一个超级汇点,并且将原图的汇点向源

[ACdream 1211 Reactor Cooling]无源无汇有上下界的可行流

题意:无源无汇有上下界的可行流 模型 思路:首先将所有边的容量设为上界减去下界,然后对一个点i,设i的所有入边的下界和为to[i],所有出边的下界和为from[i],令它们的差为dif[i]=to[i]-from[i],根据流量平衡原理,让出边和入边的下界相抵消,如果dif[i]>0,说明入边把出边的下界抵消了,还剩下dif[i]的流量必须要流过来(否则不满足入边的下界条件),这时从源点向i连一条容量为dif[i]的边来表示即可,如果dif[i]<0,同理应该从i向汇点连一条容量为-dif[i

ZOJ 2314 带上下界的可行流

对于无源汇问题,方法有两种. 1 从边的角度来处理. 新建超级源汇, 对于每一条有下界的边,x->y, 建立有向边 超级源->y ,容量为x->y下界,建立有向边 x-> 超级汇,容量为x->y下界.建立有向边 x->y,容量为x->y的上界减下界. 2 从点的角度来处理. 新建超级源汇,对于每个点流进的下界和为 in, 流出此点的下界和为out.如果in > out. 建立有向边 超级源->i,容量为in-out.反之,建立有向边 i->超级汇

【UVALive - 5131】Chips Challenge(上下界循环费用流)

Description A prominent microprocessor company has enlisted your help to lay out some interchangeable components(widgets) on some of their computer chips. Each chip’s design is an N × N square of slots. Oneslot can hold a single component, and you ar

2014多校7(1006)hdu4940(有上下界的最大流)

Destroy Transportation system Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 114    Accepted Submission(s): 83 Problem Description Tom is a commander, his task is destroying his enemy's tran