[usOJ5940]道路设计

题目

传送门

题目描述
最近市区的交通拥挤不堪,交通局长如果再不能采取措施改善这种糟糕的状况,他就不可避免地要被免职了。市区的道路已经修得够多了,总共 n n n 个站点,已经修了 n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n1) 条道路,也就是任意两个站点都有一条道路连接。但因为道路都很窄,也无法再加宽,所以所有的道路都是单向的。现在,交通局长认为导致交通拥堵的原因之一是存在环路。他决定改变一些道路的方向,使得不存在任何环路。但是,如果改动数量太多,市民们又要打电话投诉了。现在,请你帮帮他,尽量改动最少的道路的方向,使得整个交通网中没有环路。

输入格式
给出一个整数 n n n ,表示有 n n n 个点。
接下来有一个 n n n n n n 列的矩阵,如果第 i i i 行第 j j j 列为 1 1 1 ,表示有一条从 i i i j j j 的单向道路,如果为 0 0 0 ,表示没有从 i i i j j j 的单向道路。

保证所有的数据合法。

输出格式
一个整数,表示最少需要改变的道路条数。

数据范围与约定
40 % 40\% 40% 的数据, n ≤ 10 n\le 10 n10
100 % 100\% 100% 的数据, n ≤ 20 n\le 20 n20

思路

有一个性质,即 n n n 个点, n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n1) 条边的有向图中不存在环的充要条件是: n n n 个点的出度或入度互不相同,出度或入度从 0 0 0 n − 1 n-1 n1 都有。

首先必须有一个点出度为 0 0 0 ,否则从任意一个点出发,沿着有向边可以一直走下去。那自然会重复访问到某个重复点,这就表明有环了。

也不能有多于一个点出度为 0 0 0 。否则这两个点之间就没有边了。

所以,必须刚好一个点的出度为 0 0 0 。而它的入度刚好是 n − 1 n-1 n1 。删掉这个点及它的 n − 1 n-1 n1 条入边,我们得到一个 n − 1 n-1 n1 点的图,刚好有 ( n − 1 ) ( n − 2 ) 2 \frac{(n-1)(n-2)}{2} 2(n1)(n2) 条边。同样的分析方法,我们知道,新图中只能有一个点出度为 0 0 0 。那意味着它原来的出度为 1 1 1 。依次类推,可以知道,出度为 2 2 2 、为 3 3 3 、……,为 n − 1 n-1 n1 的点都只有唯一一个。

状压 d p \tt{dp} dp ,设 f ( x ) f(x) f(x) 表示达到 x x x 这个状态需改变的边数量的最小值。 x x x 的二进制中,为 1 1 1 的位,表示其代表的点入度已经达到最大值,为 0 0 0 的位,表示该点还没有考虑。

比如 x x x 中为 1 1 1 的位有 3 3 3个,则这三个点的入度是 n − 1 , n − 2 , n − 3 n-1,n-2,n-3 n1,n2,n3 。这个 x x x 表示的是组合,即这三个点无论谁入度为 n − 1 n-1 n1 ,谁入度为 n − 2 n-2 n2 ,都可以更新 f ( x ) f(x) f(x)

这三个点都已经达到了入度的最大值,它们在未来的图中都不再起作用,可以认为他们被删掉了。

此时,任选 x x x 的二进制中为 0 0 0 的位,设该位为第 j j j 位,其他的 0 0 0 位都向它连有向边。

这里统计需要改变方向的边的数量,记为 w j w_j wj 。 则 f ( x ) = min ⁡ [ f ( x + 2 j ) + w j ] f(x)=\min[f(x+2^j)+w_j] f(x)=min[f(x+2j)+wj]

w j w_j wj 可以预处理的。总复杂度 O ( n 2 n ) \mathcal O(n2^n) O(n2n) w w w 的计算,均摊是 O ( n 2 n ) \mathcal O(n2^n) O(n2n) ,证明略)。

代码

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;

const int MaxN = 20;
int g[MaxN][MaxN], n;

int dp[1<<MaxN], w[MaxN][1<<MaxN];
int main(){
	scanf("%d",&n);
	for(int i=0; i<n; ++i){
		for(int j=0; j<n; ++j)
			scanf("%d",&g[i][j]);
		g[i][i] = 1;
	}
	for(int i=0; i<n; ++i){
		w[i][(1<<n)-1] = 0;
		for(int S=(1<<n)-2; ~S; --S){
			int bit;
			for(bit=0; bit<n; ++bit)
				if(not(S>>bit&1)) break;
			w[i][S] = w[i][S^(1<<bit)]+(g[bit][i]^1);
		}
	}
	for(int S=1; S<(1<<n); ++S){
		dp[S] = 1<<MaxN;
		for(int i=0; i<n; ++i)
			if(S>>i&1)
				dp[S] = min(dp[S],dp[S^(1<<i)]+w[i][S^(1<<i)]);
	}
	printf("%d\n",dp[(1<<n)-1]);
	return 0;
}
weixin028基于微信小程序小说阅读器设计+ssm后端毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值