【HDU】4888 Redraw Beautiful Drawings 网络流【推断解是否唯一】

【HDU】4888 Redraw Beautiful Drawings 网络流【推断解是否唯一】

大家好,又见面了,我是全栈君。

传送门:题目分析:

比赛的时候看出是个网络流,可是没有敲出来。各种反面样例推倒自己(究其原因是不愿意写暴力推断的)。。

首先是简单的行列建边。源点向行建边。容量为该行元素和,汇点和列建边。容量为该列元素和。全部的行向全部的列建边,容量为K。

跑一次最大流。满流则有解,否则无解。

接下来是推断解是否唯一。

这个题解压根没看懂。还是暴力大法好。

最简单的思想就是枚举在一个矩形的四个端点。设A、D为主对角线上的端点。B、C为副对角线上的端点。仅仅要A、D不等于k且B、C不等于0,那么有解。

相应的仅仅要B、C不等于k且A、D不等于0,那么相同有解。

那么。枚举全部的矩形复杂度高达O(N^4),太大了,我们须要减少复杂度。

那么我们该怎样减少复杂度?

如今我们设立一个二维数组can[ i ][ j ]表示当前行之前的行中存在一行满足列i的元素不等于0且列j的元素不等于k,那么can[ i ][ j ] = 1,假设本行同样列满足列i不等于k且列j不等于0。那么说明存在多解。否则将can[ j ][ i ]标记为1。然后继续扫描。

上面的过程推断得出是唯一解后,输出解。( i , j )相应的元素为行i到列j的反向边的流量。

代码例如以下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REP( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REV( i , a , b ) for ( int i = a - 1 ; i >= b ; -- i )
#define FOV( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define CLR( a , x ) memset ( a , x , sizeof a )
#define CPY( a , x ) memcpy ( a , x , sizeof a )

const int MAXM = 405 ;
const int MAXN = 1005 ;
const int MAXQ = 500000 ;
const int MAXE = 500000 ;
const int INF = 0x3f3f3f3f ;

struct Edge {
	int v , c , n ;
	Edge () {}
	Edge ( int v , int c , int n ) : v ( v ) , c ( c ) , n ( n ) {}
} ;

struct NetWork {
	Edge E[MAXE] ;
	int H[MAXN] , cntE ;
	int d[MAXN] , cur[MAXN] , num[MAXN] , pre[MAXN] ;
	int Q[MAXQ] , head , tail ;
	int n , m , k ;
	int s , t , nv ;
	int flow ;
	
	int row[MAXN] , col[MAXN] ;
	int G[MAXM][MAXM] ;
	int can[MAXM][MAXM] ;
	
	void init () {
		cntE = 0 ;
		CLR ( H , -1 ) ;
	}
	
	void addedge ( int u , int v , int c ) {
		E[cntE] = Edge ( v , c , H[u] ) ;
		H[u] = cntE ++ ;
		E[cntE] = Edge ( u , 0 , H[v] ) ;
		H[v] = cntE ++ ;
	}
	
	void rev_bfs () {
		CLR ( d , -1 ) ;
		CLR ( num , 0 ) ;
		head = tail = 0 ;
		Q[tail ++] = t ;
		d[t] = 0 ;
		num[d[t]] = 1 ;
		while ( head != tail ) {
			int u = Q[head ++] ;
			for ( int i = H[u] ; ~i ; i = E[i].n ) {
				int v = E[i].v ;
				if ( ~d[v] )
					continue ;
				d[v] = d[u] + 1 ;
				num[d[v]] ++ ;
				Q[tail ++] = v ;
			}
		}
	}
	
	int ISAP () {
		CPY ( cur , H ) ;
		rev_bfs () ;
		flow = 0 ;
		int u = pre[s] = s , i ;
		while ( d[s] < nv ) {
			if ( u == t ) {
				int f = INF , pos ;
				for ( i = s ; i != t ; i = E[cur[i]].v )
					if ( f > E[cur[i]].c ) {
						f = E[cur[i]].c ;
						pos = i ;
					}
				for ( i = s ; i != t ; i = E[cur[i]].v ) {
					E[cur[i]].c -= f ;
					E[cur[i] ^ 1].c += f ;
				}
				flow += f ;
				u = pos ;
			}
			for ( i = cur[u] ; ~i ; i = E[i].n )
				if ( E[i].c && d[u] == d[E[i].v] + 1 )
					break ;
			if ( ~i ) {
				cur[u] = i ;
				pre[E[i].v] = u ;
				u = E[i].v ;
			}
			else {
				if ( 0 == ( -- num[d[u]] ) )
					break ;
				int mmin = nv ;
				for ( i = H[u] ; ~i ; i = E[i].n )
					if ( E[i].c && mmin > d[E[i].v] ) {
						cur[u] = i ;
						mmin = d[E[i].v] ;
					}
				d[u] = mmin + 1 ;
				num[d[u]] ++ ;
				u = pre[u] ;
			}
		}
		return flow ;
	}
	
	void put () {
		printf ( "Unique\n" ) ;
		FOR ( i , 1 , n )
			FOR ( j , 1 , m )
				printf ( "%d%c" , G[i][j] , j < m ? ' ' : '\n' ) ;
	}
	
	void build () {
		FOR ( u , 1 , n )
			for ( int i = H[u] ; ~i ; i = E[i].n )
				if ( E[i].v )
					G[u][E[i].v - n] = E[i ^ 1].c ;
	}
	
	int check () {
		CLR ( can , 0 ) ;
		FOR ( r , 1 , n )
			FOR ( i , 1 , m )
				FOR ( j , i + 1 , m ) {
					int tmp1 = 0 , tmp2 = 0 ;
					if ( G[r][i] != k && G[r][j] != 0 ) {
						if ( can[i][j] )
							return 1 ;
						tmp1 = 1 ;
					}
					if ( G[r][i] != 0 && G[r][j] != k ) {
						if ( can[j][i] )
							return 1 ;
						tmp2 = 1 ;
					}
					if ( tmp1 )
						can[j][i] = tmp1 ;
					if ( tmp2 )
						can[i][j] = tmp2 ;
				}
		return 0 ;
	}
	
	void solve () {
		int sum1 = 0 ;
		int sum2 = 0 ;
		init () ;
		s = 0 ;
		t = n + m + 1 ;
		nv = t + 1 ;
		FOR ( i , 1 , n ) {
			scanf ( "%d" , &row[i] ) ;
			addedge ( s , i , row[i] ) ;
			sum1 += row[i] ;
		}
		FOR ( i , 1 , m ) {
			scanf ( "%d" , &col[i] ) ;
			addedge ( i + n , t , col[i] ) ;
			sum2 += col[i] ;
		}
		FOR ( i , 1 , n )
			FOR ( j , 1 , m )
				addedge ( i , n + j , k ) ;
		ISAP () ;
		if ( flow != sum1 || flow != sum2 ) {
			printf ( "Impossible\n" ) ;
			return ;
		}
		build () ;
		int multi = check () ;
		if ( multi )
			printf ( "Not Unique\n" ) ;
		else
			put () ;
	}
} nw ;

int main () {
	while ( ~scanf ( "%d%d%d" , &nw.n , &nw.m , &nw.k ) )
		nw.solve () ;
	return 0 ;
}

题解依然没看懂,可是DFS的方法会了。思想和上面的类似,用回溯的思想找环。可知长度等于2的环是不可行的(由于两个点都是自己),必须是长度大于等于4的环(能形成环必然长度为偶数)。我们仅仅要对每一行回溯搜索,假设沿着行到列还有流容量能够降低(属于的元素能够变大)或者列到行还有容量能够降低(即行到列还有容量能够添加,属于的元素能够变小)的边走能找到环就说明多解。

代码例如以下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REP( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REV( i , a , b ) for ( int i = a - 1 ; i >= b ; -- i )
#define FOV( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define CLR( a , x ) memset ( a , x , sizeof a )
#define CPY( a , x ) memcpy ( a , x , sizeof a )

const int MAXM = 1005 ;
const int MAXN = 1005 ;
const int MAXQ = 2000000 ;
const int MAXE = 2000000 ;
const int INF = 0x3f3f3f3f ;

struct Edge {
	int v , c , n ;
	Edge () {}
	Edge ( int v , int c , int n ) : v ( v ) , c ( c ) , n ( n ) {}
} ;

struct NetWork {
	Edge E[MAXE] ;
	int H[MAXN] , cntE ;
	int d[MAXN] , cur[MAXN] , num[MAXN] , pre[MAXN] ;
	int Q[MAXQ] , head , tail ;
	int n , m , k ;
	int s , t , nv ;
	int flow ;
	
	int row[MAXN] , col[MAXN] ;
	int G[MAXM][MAXM] ;
	int vis[MAXN] ;
	
	void init () {
		cntE = 0 ;
		CLR ( H , -1 ) ;
	}
	
	void addedge ( int u , int v , int c ) {
		E[cntE] = Edge ( v , c , H[u] ) ;
		H[u] = cntE ++ ;
		E[cntE] = Edge ( u , 0 , H[v] ) ;
		H[v] = cntE ++ ;
	}
	
	void rev_bfs () {
		CLR ( d , -1 ) ;
		CLR ( num , 0 ) ;
		head = tail = 0 ;
		Q[tail ++] = t ;
		d[t] = 0 ;
		num[d[t]] = 1 ;
		while ( head != tail ) {
			int u = Q[head ++] ;
			for ( int i = H[u] ; ~i ; i = E[i].n ) {
				int v = E[i].v ;
				if ( ~d[v] )
					continue ;
				d[v] = d[u] + 1 ;
				num[d[v]] ++ ;
				Q[tail ++] = v ;
			}
		}
	}
	
	int ISAP () {
		CPY ( cur , H ) ;
		rev_bfs () ;
		flow = 0 ;
		int u = pre[s] = s , i ;
		while ( d[s] < nv ) {
			if ( u == t ) {
				int f = INF , pos ;
				for ( i = s ; i != t ; i = E[cur[i]].v )
					if ( f > E[cur[i]].c ) {
						f = E[cur[i]].c ;
						pos = i ;
					}
				for ( i = s ; i != t ; i = E[cur[i]].v ) {
					E[cur[i]].c -= f ;
					E[cur[i] ^ 1].c += f ;
				}
				flow += f ;
				u = pos ;
			}
			for ( i = cur[u] ; ~i ; i = E[i].n )
				if ( E[i].c && d[u] == d[E[i].v] + 1 )
					break ;
			if ( ~i ) {
				cur[u] = i ;
				pre[E[i].v] = u ;
				u = E[i].v ;
			}
			else {
				if ( 0 == ( -- num[d[u]] ) )
					break ;
				int mmin = nv ;
				for ( i = H[u] ; ~i ; i = E[i].n )
					if ( E[i].c && mmin > d[E[i].v] ) {
						cur[u] = i ;
						mmin = d[E[i].v] ;
					}
				d[u] = mmin + 1 ;
				num[d[u]] ++ ;
				u = pre[u] ;
			}
		}
		return flow ;
	}
	
	void put () {
		printf ( "Unique\n" ) ;
		FOR ( u , 1 , n )
			for ( int i = H[u] ; ~i ; i = E[i].n ) {
				int v = E[i].v ;
				G[u][v - n] = E[i ^ 1].c ;
			}
		FOR ( i , 1 , n )
			FOR ( j , 1 , m )
				printf ( "%d%c" , G[i][j] , j < m ? ' ' : '\n' ) ;
	}
	
	int dfs ( int u , int fa ) {
		if ( vis[u] )
			return 1 ;
		vis[u] = 1 ;
		for ( int i = H[u] ; ~i ; i = E[i].n ) {
			int v = E[i].v ;
			if ( v != fa && E[i].c && v != s && v != t )
				if ( dfs ( v , u ) )
					return 1 ;
		}
		vis[u] = 0 ;
		return 0 ;
	}
	
	void solve () {
		int sum1 = 0 ;
		int sum2 = 0 ;
		init () ;
		s = 0 ;
		t = n + m + 1 ;
		nv = t + 1 ;
		FOR ( i , 1 , n ) {
			scanf ( "%d" , &row[i] ) ;
			addedge ( s , i , row[i] ) ;
			sum1 += row[i] ;
		}
		FOR ( i , 1 , m ) {
			scanf ( "%d" , &col[i] ) ;
			addedge ( i + n , t , col[i] ) ;
			sum2 += col[i] ;
		}
		FOR ( i , 1 , n )
			FOR ( j , 1 , m )
				addedge ( i , n + j , k ) ;
		ISAP () ;
		if ( flow != sum1 || flow != sum2 ) {
			printf ( "Impossible\n" ) ;
			return ;
		}
		int flag = 1 ;
		CLR ( vis , 0 ) ;
		FOR ( i , 1 , n ) {
			if ( !flag )
				break ;
			if ( dfs ( i , 0 ) )
				flag = 0 ;
		}	
		if ( !flag )
			printf ( "Not Unique\n" ) ;
		else
			put () ;
	}
} nw ;

int main () {
	while ( ~scanf ( "%d%d%d" , &nw.n , &nw.m , &nw.k ) )
		nw.solve () ;
	return 0 ;
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/116558.html原文链接:https://javaforall.net

(0)
上一篇 2022年1月19日 下午7:00
下一篇 2022年1月19日 下午7:00


相关推荐

  • java 实现多态_Java多态的实现原理

    java 实现多态_Java多态的实现原理0.前言多态在Java技术里有很重要的地位,在面试中也会经常被问到。多态的使用大家应该都比较了解,但是多态的实现原理就有点抽象了,查了很多很多资料,连续几天断断续续的看,有时候看着看着就走神了。毕竟太抽象,哈哈~不过依然硬着头皮看下来了(也不知道看了多少遍),并且将很多资料里关于多态的知识进行了整理(添添加加删删减减了很久,也把重点根据自己的理解用红字标出),便有了这篇文章。通过这篇文章相信可以帮…

    2022年7月7日
    20
  • Ant 执行 YUICompressor

    Ant 执行 YUICompressorAnt执行YUICompressor任务压缩JavaScript和CSS文件,解决中文乱码问题,增加源文件字符编码集设定标签:javascriptantcss任务encodingnull2012-04-0510:465376人阅读评论(4)收藏举报分类:Java(14)Ant版权声明:本文为博主原创文章,未经博主允许…

    2022年7月18日
    15
  • uni-app 人脸识别分析及实现(前端) + nvue开发源码

    uni-app 人脸识别分析及实现(前端) + nvue开发源码1 app 开发一个人脸识别 实现刷脸功能首先要考虑的是实现流程 1 打开摄像头 自动读取照片 传输给后端 后端交由第三发或自主开发来识别 返回结果 相识度比 2 打开摄像头 自动读取视频 传输给后端 后端通过解析视频 截取图片交由第三发或自主开发来识别 返回结果 相识度比 那么通过分析 我们只需要做两步骤 打开摄像头和自动读取视频或照片 2 打开摄像头分步骤分析 打开摄像头 并展示视频效果在 html 上 目前有两种方式 2 1 使用 camera 组件进行 借用 createCa

    2026年3月19日
    2
  • bash组织成树数据结构

    bash组织成树数据结构

    2022年1月14日
    43
  • flask框架的使用

    flask框架的使用文章目录一 路由二 常用的 HTTP 方法三 构造 URL 函数四 MVC 设计模型五 渲染模板一 路由路由 处理 URL 和视图函数的这种关系 访问 index 则执行 index 视图函数 fromflaskimp Flask name app route defhello world return HelloWorld 可变 动态 app route user username de username

    2026年3月26日
    1
  • MySQL删除表数据 MySQL清空表命令 3种方法

    MySQL删除表数据 MySQL清空表命令 3种方法一、MySQL清空表数据命令:truncateSQL语法:truncatetable表名注意:不能与where一起使用。 truncate删除数据后是不可以rollback的。 truncate删除数据后会重置Identity(标识列、自增字段),相当于自增列会被置为初始值,又重新从1开始记录,而不是接着原来的ID数。 truncate删除数据后不写服务器log,整体删除速度快。 truncate删除数据后不激活trigger(触发器)。二、MySQL删除表命令:

    2022年6月4日
    74

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号