【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 ;
}

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • OLAP组件选型[通俗易懂]

    OLAP组件选型[通俗易懂]OLAP组件选型一、OLAP简介1、olap准则2、OLAP场景的关键特征3、与oltp比较二、开源引擎1、Hive2、sparkSQL3、presto4、kylin5、impala6、druid7、Greeplum8、clickhouse三、选型要求1、实时性要求较高,对接kafka,实时查询数据2、可以接入hive数据3、单表查询数据较多,较少的join,在数仓中完成宽表构建一、OLAP简介说起OLAP要追溯到1993年。1、olap准则准则1OLAP模型必须提供多维概念视图准则

  • java版我的世界免费获取,持续更新~

    java版我的世界免费获取,持续更新~我的世界:Java版将与基岩版”地形”生成统一!1.18的隐藏更新!保持了JAVA版一致?事实上若你足够了解JAVA版的种子,你会发现其实很多不同的种子代码,会得到的是一个完全一致的世界。04问题四:未来未来JAVA版我的世界,和基岩版的Minecraft,它们真的可能变得完全一致!05问题五:Mojang为什么要让地形统一?一直以来,基岩版最大的魅力就。我的世界:java版免费披风,账号迁移轮到我了,快来看看怎么操作导致启动器显示我没有购买Minecraft,只有试玩资格。只有你选择微软账

  • 详解RPN网络[通俗易懂]

    详解RPN网络[通俗易懂]引言RPN(RegionProposalNetwork)是Faster-RCNN网络用于提取预选框(也就是RCNN中使用selectivesearch算法进行RegionProposal的部分),我们知道RCNN及Fast-RCNN中一个性能瓶颈就是提取预选框的部分,而RPN很好地对这个部分进行了优化,原因在于它将卷积神经网络引入了进来,使用特征提取的形式生成出预选框的位置从而降低了selectivesearch算法带来的计算时间上的开销。RPN(RegionProposalNetwor

  • 最简单的历史Hibernate获得短暂的

    最简单的历史Hibernate获得短暂的

    2021年12月30日
  • python常用模块大全_日常办公会用到的python模块

    python常用模块大全_日常办公会用到的python模块mathmath.ceil(a):用来返回≥a的最小整数math.floor(a):用来返回≤a的最大整数round(a[,b])如果没有参数b,只有a,round()作用是四舍五入如果

  • Kafka集群搭建 以及命令「建议收藏」

    Kafka集群搭建 以及命令「建议收藏」好久没有写博客了有点懒赶紧补一下Kafka集群搭建测试机三个节点hadoop1hadoop2hadoop3这三个节点安装了独立的zookeeper且我其他hadoophbase等元数据信息都在上面我不用Kafka自带的zookeeper本次的版本是:/kafka_2.10-0.10.0.1修改配置:kafka_2.10-0.10.0.1/conf

发表回复

您的电子邮箱地址不会被公开。

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