• 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("
    ");
    	}
    }
    
    //保存 第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
    
    ");
    			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
    
    ");
    			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("
    ");
    		}
    		printf("
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    串口调试助手的源码分析,子对话框部分
    vc 使用了SerialPort类的串口通信软件分析
    Web前端研发工程师编程能力飞升之路
    vc mscomm串口通信使用了CButtonST按钮类软件分析
    vc 串口精灵软件分析
    c#串口完全接收程序
    Windows Server 2003操作系统中禁用Internet Explorer增强的安全特性
    Window下查看进程端口号
    面向对象的设计原则(2)——依赖注入
    面向对象的设计原则(1)
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4890127.html
Copyright © 2020-2023  润新知