poj_2396 有上下界的网络流
题目大意
一个mxn的矩阵,给出矩阵中每一行的和sh[1,2...m]以及每一列的数字的和目sv[1,2...n],以及矩阵中的一些元素的范围限制,比如a[1][2] > 1, a[2][3] < 4, a[3][3] = 10等等,且要求矩阵中的每个元素值非负。求出,是否存在满足所有给出的限制条件的矩阵,若存在输出。
题目分析
这么多限制条件。。开始只能想到暴力解法+剪枝。这他妈得需要多大的脑洞才能无中生有的想到网络流解法? 不过,给出网络流的想法之后发现采用网络流解法确实很合理,很简单(唯一不好的是建图太费劲!!)。
考虑将每行视为一个节点,每列视为一个节点,这样就有m个行节点和n个列节点,m个行节点和n个列节点之间的直接通路mxn个,这些通路上的流量视为我们要求的矩阵的元素值。
那么,如何利用限制条件来构造图?
添加一个源点s和一个汇点t。将每一行的数字和视为从s流入该行对应节点的上下界相同的流,每一列的数字和视为从该列对应的节点流到t的上下界相同的流。
然后,将矩阵元素(i, j)的范围限制在图中用行节点i到列节点j之间的一条有流量上下界的边表示。那么,行节点i的流入量(只有s点流入行节点)总和为该行的数字总和,同时该行节点连接到n个列节点,这n条边上的流量分别对应元素[i,1][i,2].... 且若该网络存在可行流我们对图节点流量和矩阵元素求和的对应。
根据矩阵元素的限制条件,构造图,该图为一个有源汇有上下界的网络流。采用求解有源汇流量有上下界的网络可行流的解法:先将有源汇转换为无源汇,然后求解可行流
。
ps: 麻蛋,写了五六个小时,中间各种bug,累感无爱。。 总结出一个规律:在程序求解多个步骤,多种因素对某一个变量的影响的时候,常常可以总结规律,不需要每次有一个因素起作用的时候都修改目标变量,可以先将各个因素/各个步骤的影响记录下来,最后再修改目标变量。
这样做,基本都是可以提高效率(降低时间复杂度和编码复杂度,简化逻辑),甚至有时候这就应该是合理的逻辑。
实现(c++)
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define MAX_NODE 250
#define MAX_EDGE_NUM 50000
#define INFINITE 1 << 25
#define min(a, b) a<b? a:b
#define max(a,b) a>b? a:b
//============================= 以下为 寻找最大网络流的 ISAP算法 部分 =============================
struct Edge{ //定义边
int from;
int to;
int w;
int next;
int rev;
bool operator == (const pair<int, int>& p){
return from == p.first && to == p.second;
}
};
Edge gEdges[MAX_EDGE_NUM]; int gEdgeCount;
int gFlow[MAX_NODE][MAX_NODE];
int gGap[MAX_NODE];
int gDist[MAX_NODE];
int gHead[MAX_NODE];
int gPre[MAX_NODE];
int gPath[MAX_NODE];
int ss, tt, sum1, sum2, m, n;
int gSource, gDestination;
void InsertEdge(int u, int v, int w){
Edge* it = find(gEdges, gEdges + gEdgeCount, pair<int, int>(u, v));
if (it != gEdges + gEdgeCount){
if (u == ss || v == tt){
it->w += w;
}
else{
it->w = w;
}
}
else{
int e1 = gEdgeCount;
gEdges[e1].from = u;
gEdges[e1].to = v;
gEdges[e1].w = w;
gEdges[e1].next = gHead[u];
gHead[u] = e1; gEdgeCount++;
int e2 = gEdgeCount;
gEdges[e2].from = v;
gEdges[e2].to = u;
gEdges[e2].w = 0;
gEdges[e2].next = gHead[v];
gHead[v] = e2; gEdges[e1].rev = e2;
gEdges[e2].rev = e1;
gEdgeCount++;
} } void Bfs(){
memset(gGap, 0, sizeof(gGap));
memset(gDist, -1, sizeof(gDist));
gGap[0] = 1;
gDist[gDestination] = 0;
queue<int>Q;
Q.push(gDestination);
while (!Q.empty()){
int n = Q.front();
Q.pop();
for (int e = gHead[n]; e != -1; e = gEdges[e].next){
int v = gEdges[e].to;
if (gDist[v] >= 0)
continue;
gDist[v] = gDist[n] + 1;
gGap[gDist[v]] ++;
Q.push(v);
}
}
} int ISAP(int n){ // n为节点的数目
Bfs();
int u = gSource;
int e, d;
int ans = 0;
while (gDist[gSource] <= n){
if (u == gDestination){ //增广
int min_flow = INFINITE;
for (e = gPath[u]; u != gSource; e = gPath[u = gPre[u]]){ //注意,先u = gPre[u], 再取 e = gPath[u]
min_flow = min(min_flow, gEdges[e].w);
}
u = gDestination;
for (e = gPath[u]; u != gSource; e = gPath[u = gPre[u]]){
gEdges[e].w -= min_flow;
gEdges[gEdges[e].rev].w += min_flow; gFlow[gPre[u]][u] += min_flow;
gFlow[u][gPre[u]] -= min_flow; //这个不能少,注意!!!!
}
ans += min_flow;
}
for (e = gHead[u]; e != -1; e = gEdges[e].next){
if (gEdges[e].w > 0 && gDist[u] == gDist[gEdges[e].to] + 1)
break;
}
if (e >= 0){ //向前推进
gPre[gEdges[e].to] = u; //前一个点
gPath[gEdges[e].to] = e;//该点连接的前一个边
u = gEdges[e].to;
}
else{
d = n;
for (e = gHead[u]; e != -1; e = gEdges[e].next){
if (gEdges[e].w > 0) //需要能够走通才行!!
d = min(d, gDist[gEdges[e].to]);
}
if (--gGap[gDist[u]] == 0) //gap优化
break; gDist[u] = d + 1; //重标号 ++gGap[gDist[u]]; //更新 gap!!
if (u != gSource)
u = gPre[u];//回溯
}
}
return ans;
} //=================================以上为 ISAP 算法 ============================ //将图打印出来,用于debug
void print_graph(int n){
for (int u = 0; u < n; u++){
printf("node %d links to ", u);
for (int e = gHead[u]; e != -1; e = gEdges[e].next)
printf("%d(flow = %d) ", gEdges[e].to, gEdges[e].w);
printf("\n");
}
} //保存 第i行第j列的数字的范围
struct Node{
int min_val;
int max_val;
};
Node gNodes[250][25]; //设置数字的范围
bool SetDataCons(Node& node, char op, int val){
if (op == '='){
if (node.max_val != -1 && node.max_val < val){
return false;
}
if (node.min_val != -1 && node.min_val > val){
return false;
}
node.max_val = node.min_val = val;
}
else if (op == '>'){
if (node.max_val != -1 && node.max_val <= val){
return false;
}
node.min_val = max(node.min_val, val + 1);
}
else if (op == '<'){
if (val <= 0){
return false;
}
if (node.min_val != -1 && node.min_val >= val){
return false;
}
if (node.max_val != -1)
node.max_val = min(node.max_val, val - 1);
else
node.max_val = val - 1;
}
return true;
} //建图
void BuildGraph(){
//注意,第i行,第j列,对应node结构体的 gNodes[i][j],并且对应,最后的图中的节点为 i和 j+m(m为行数)
for (int i = 1; i <= m; i++){
for (int j = 1; j <= n; j++){
if (gNodes[i][j].max_val == -1){
if (gNodes[i][j].min_val == -1){
InsertEdge(i, j + m, INFINITE);
}
else{
sum1 += gNodes[i][j].min_val;
InsertEdge(i, tt, gNodes[i][j].min_val);
InsertEdge(ss, j + m, gNodes[i][j].min_val);
InsertEdge(i, j + m, INFINITE); gFlow[i][j + m] = gNodes[i][j].min_val; //边的下限,先加入到最后的边的流量中
}
}
else {
if (gNodes[i][j].min_val == -1){
InsertEdge(i, j + m, gNodes[i][j].max_val); }
else{
sum1 += gNodes[i][j].min_val; InsertEdge(i, tt, gNodes[i][j].min_val);
InsertEdge(ss, j + m, gNodes[i][j].min_val); if (gNodes[i][j].max_val > gNodes[i][j].min_val)
InsertEdge(i, j + m, gNodes[i][j].max_val - gNodes[i][j].min_val); gFlow[i][j + m] = gNodes[i][j].min_val;//边的下限,先加入到最后的边的流量中
}
}
}
}
}
int main(){ int c, w, u, v, cas, val;
char op;
scanf("%d", &cas);
while (cas --){
scanf("%d %d", &m, &n);
memset(gNodes, -1, sizeof(gNodes));
gEdgeCount = 0;
memset(gEdges, 0, sizeof(gEdges)); memset(gFlow, 0, sizeof(gFlow));
memset(gHead, -1, sizeof(gHead)); gSource = 0, gDestination = n + m + 1;
ss = n + m + 2, tt = n + m + 3;
sum1 = 0, sum2 = 0;
bool valid_data = true;
//输入的过程中,先插入一些边 从 SS 出发的
for (int i = 1; i <= m; i++){
scanf("%d", &w);
if (valid_data == false)
continue;
if (w < 0){
valid_data = false;
}
sum1 += w;
InsertEdge(ss, i, w);
} InsertEdge(gSource, tt, sum1);
//输入的过程中,先插入一些边 到达 TT的
for (int i = m + 1; i <= m + n; i++){
scanf("%d", &w);
if (valid_data == false)
continue;
if (w < 0)
valid_data = false;
sum2 += w;
InsertEdge(i, tt, w);
} InsertEdge(ss, gDestination, sum2); if (sum1 != sum2){
valid_data = false;
} sum1 = sum2 = (sum1 + sum2); //设置 矩阵中数字的范围
scanf("%d", &c);
for (int i = 0; i < c; i++){
scanf("%d %d %c %d", &u, &v, &op, &val);
if (valid_data == false)
continue; if (w < 0){
if (op == '=' || op == '<')
valid_data = false;
continue;
} if (u == 0){
if (v == 0){
for (int i = 1; i <= m; i++){
for (int j = 1; j <= n; j++){
if (valid_data == false){
break;
}
valid_data = SetDataCons(gNodes[i][j],op, val);
}
}
}
else{
for (int i = 1; i <= m; i++){
if (valid_data == false){
break;
}
valid_data = SetDataCons(gNodes[i][v], op, val);
}
}
}
else{
if (v == 0){
for (int j = 1; j <= n; j++){
if (valid_data == false){
break;
}
valid_data = SetDataCons(gNodes[u][j], op, val);
}
}
else{
valid_data = SetDataCons(gNodes[u][v], op, val);
}
}
}
if (valid_data == false){
printf("IMPOSSIBLE\n\n");
continue;
}
//插入 t-->s的边,容量无穷大,使得有源汇的网络变为无源汇的网络
InsertEdge(gDestination, gSource, INFINITE);
//建图
BuildGraph(); gSource = ss;
gDestination = tt;
// print_graph(n + m + 4);
//找最大流
int ans = ISAP(n + m + 4); if (ans != sum1){
printf("IMPOSSIBLE\n\n");
continue;
}
//输出各个边的流量, 从图中节点i到节点j的流量,即为 矩阵 中 [i][j-m]的数字的大小
for (int i = 1; i <= m; i++){
for (int j = m+1; j <= m+n; j++){
printf("%d ", gFlow[i][j]);
}
printf("\n");
}
printf("\n");
}
return 0;
}
poj_2396 有上下界的网络流的更多相关文章
- ACM/ICPC 之 有流量上下界的网络流-Dinic(可做模板)(POJ2396)
//有流量上下界的网络流 //Time:47Ms Memory:1788K #include<iostream> #include<cstring> #include<c ...
- SGU 194. Reactor Cooling(无源汇有上下界的网络流)
时间限制:0.5s 空间限制:6M 题意: 显然就是求一个无源汇有上下界的网络流的可行流的问题 Solution: 没什么好说的,直接判定可行流,输出就好了 code /* 无汇源有上下界的网络流 * ...
- 【ZOJ2314】Reactor Cooling(有上下界的网络流)
前言 话说有上下界的网络流好像全机房就我一个人会手动滑稽,当然这是不可能的 Solution 其实这道题目就是一道板子题,主要讲解一下怎么做无源无汇的上下界最大流: 算法步骤 1.将每条边转换成0~u ...
- ZOJ 2314 有上下界的网络流
problemCode=2314">点击打开链接 题意:给定m条边和n个节点.每条边最少的流量和最多的流量.保证每一个节点的出入流量和相等,问能够形成吗,能够则输出每条边的流量 思路: ...
- 【BZOJ2502】清理雪道 有上下界的网络流 最小流
[BZOJ2502]清理雪道 Description 滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降 ...
- 【2018沈阳赛区网络预选赛J题】Fantastic Graph 【有上下界的网络流】
要补的题太多了导致最近没写博客(好吧是我懒) 题目链接https://nanti.jisuanke.com/t/31447 题意 给出一个二分图,问能否挑选出一些边,使得每个点的度数都在[L,R]这个 ...
- ZOJ2314 Reactor Cooling(有上下界的网络流)
The terrorist group leaded by a well known international terrorist Ben Bladen is buliding a nuclear ...
- ZOJ 2314 Reactor Cooling 带上下界的网络流
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1314 题意: 给n个点,及m根pipe,每根pipe用来流躺液体的, ...
- BZOJ 2406 二分+有上下界的网络流判定
思路: 求出每行的和 sum_row 每列的和 sum_line 二分最后的答案mid S->i 流量[sum_row[i]-mid,sum_row[i]+mid] i->n+j ...
随机推荐
- Java并发(一)Java并发/多线程教程
在过去一台电脑只有单个CPU,并且在同一时间只能执行单个程序.后来出现的"多任务"意味着电脑在可以同时执行多个程序(AKA任务或者进程).虽然那并不是真正意义上的"同时& ...
- Adnroid 反编译APK
http://blog.csdn.net/vipzjyno1/article/details/21039349 http://blog.csdn.net/zx19899891/article/deta ...
- TP5 display()
tp3.x $this->display(); tp5 return $this->fetch();
- 第一关练习题统计网站最大访问量sed法,隐藏知识数组下标不能重复
1.1.1 获取日志的最大top10,排序 获取两列到新的文件中第一次处理 sed截取字符串中间的内容,sed不支持贪婪匹配.找出图片在的列和图片大小到test1文件 本题需要输出三个指标:[访问次数 ...
- WHAT EXACTLY IS WASM ?!
终于, 我入门了当初很仇视的技术.... 什么是WebAssembly? WebAssembly或WASM是一个编译器目标(由编译器生成的代码),具有二进制格式,允许我们在浏览器上执行C,C ++和R ...
- HTML(二):表格元素
表格元素的作用:用来格式化显示数据. 一.表格的基本结构 表格的基本语法:<TABLE border="设置表格边框尺寸大小" width="" cell ...
- 查看nginx cache命中率
一.在http header上增加命中显示 nginx提供了$upstream_cache_status这个变量来显示缓存的状态,我们可以在配置中添加一个http头来显示这一状态,达到类似squid的 ...
- 使用JPedal取代PDFBox
http://wanggp.iteye.com/blog/1144177 ———————————————————————————————————————————————— 之前都是使用PDFBOX0. ...
- js类型转换 之 转字符串及布尔类型
上一篇我们讲到了如何转数字类型,今天总结一下转字符串及布尔类型的方法: 转字符串方法主要有: toString(); String(); 具体的用法如下表格所示: 方法 例子 返回值 说明 toStr ...
- (转)x264代码详细阅读之x264.c,common.c,encoder.c
转自:http://alphamailpost.blog.163.com/blog/static/201118081201281103931932/ x264代码详细阅读第一之x264.chttp:/ ...