【搜索】逛公园

逛公园

(park.pas/c/cpp/in/out)

时限:1 sec | 内存:64 MB

背景

   SC theme Park 终于开业了,可爱的平平小朋友很荣幸的成为第一个游客。

    公园设计强调,复杂就是美。scp大老板给他的公园设计了一个极其复杂的布局:

由于公园极大,而景点又很多,scp大老板在任意的两个景点之间都建造了一条星光小道,而且还为每条小道制定了方向。

 

题目描述

    现在,平平从scp大老板那里得知公园总共有N个景点,并且已经知道了每一条星光小道的方向,但由于平平的方向感极差而RP又极低,于是一旦公园中出现回路,即存在环,平平便会迷路,并且无论怎么走都走不出去。

    这样,scp大老板可就伤透脑筋了。为了使平平不会迷路,scp大老板决定改变其中M条星光小道的方向使得公园里不存在回路,但scp大老板又希望改变的小道的条数最少。由于很忙,腾不出时间,scp大老板只好请教即将参加noip的你。(注意:任意两个景点之间有且只有一条星光小道,且任意两条小道都是不相通的,即不能从一条小道不经过景点直接到达另一条小道)。

 

输入

第一行有一个整数N,表示有N个景点。

接下来是一张N*N的矩阵,第i+1行第j列表示有无从景点i指向景点j的星光小道(0表示没有,1表示有)。

 

输出

输出仅包括一行,即M的最小值。

 

输入样例 ( park.in)

4

0 0 0 0

1 0 1 0

1 0 0 1

1 1 0 0

 

输出样例 ( park.out)

1

 

数据规模

对于30%数据,1<=N<=10;

对于100%数据,1<=N<=20.




这套题比较难,实在没能做完。


有一个结论,完全图中如果没有环。那么我们将所有点按照出度排序,必然是0、1、2、3、4....n-1。


证明:必定有一个点出度为0,否则有环。因为是完全图,因此其他所有点必定向出度为0的点连一条边,因此出度至少为1。

而出度为1的点必定存在,否则有环。因此其它点出度至少为2。。依此类推。可证得所有点出度依次为0、1、2...n-1


此时便可以搜索。

搜索有两种思路比较清晰。

一种是我一开始采用的。TLE30。按出度升序枚举点i,每次枚举时遍历一次出度较小的点j,如果i到j无边,说明现在这条边是改变而来的的,答案加1。

另一种TLE60。按入度升序枚举点i,并把答案加上入度(这不是真正的入度,稍后说明),遍历所有的点j,如果i到j有边,则将j的入度减1,表示只记录原来没有而是新变来的入边。


TLE30:

#include <cstdio>
#include <algorithm>

long c = 0;
long ans = 0x7f7f7f7f;
char map[30][30];
bool used[30];
long bs[30];
long stack[30];
long top = 0;
long n;

struct node
{
	long i;
	long c;
	bool operator<(const node& n2)const{return c<n2.c;}
}chudu[30];

void dfs(long nc,long c)
{
	if (nc >= ans)
		return;
	if (c == n+1)
	{
		ans = ans<nc?ans:nc;
		return;
	}
	for (long ii=1;ii<n+1;ii++)
	{
		long i = chudu[ii].i;
		if (!used[i])
		{
			used[i] = 1;
			stack[++top] = i;
			long tmp = 0;
			for (long j=1;j<c;j++)
			{
				tmp += map[stack[j]][i];
			}
			dfs(nc+tmp,c+1);
			top --;
			used[i] = 0;
		}
	}
}

int main()
{
	freopen("park.in","r",stdin);
	freopen("park.out","w",stdout);
	scanf("%ld",&n);
	for (long i=1;i<n+1;i++)
	{
		chudu[i].i = i;
		for (long j=1;j<n+1;j++)
		{
			do map[i][j] = getchar()-'0';
			while (map[i][j]!=0&&map[i][j]!=1);
			chudu[i].c += map[i][j];
		}
	}
	std::sort(chudu+1,chudu+n+1);
	dfs(0,1);
	printf("%ld",ans);
	return 0;
}
TLE60:
#include <cstdio>

long ans = 0;
long minans = 0x7f7f7f7f;
long into[60];
bool used[60];
char map[60][60];
long n;

void dfs(long u)
{
	if (u == n)
	{
		minans = minans<ans?minans:ans;
		return;
	}
	for (long i=1;i<n+1;i++)
	{
		if (!used[i])
		{
			ans += into[i];
			if (ans < minans)
			{
    			used[i] = true;
    			for (long j=1;j<n+1;j++)
    				if (map[i][j]) into[j]--;
              
    			dfs(u+1);
    
    			for (long j=1;j<n+1;j++)
    				if (map[i][j]) into[j]++;
    			used[i] = false;
            }
			ans -= into[i];
		}
	}
}

int main()
{
	freopen("park.in","r",stdin);
	freopen("park.out","w",stdout);

	scanf("%ld",&n);
	for (long i=1;i<n+1;i++)
	{
		for (long j=1;j<n+1;j++)
		{
			do map[i][j] = getchar() - '0';
			while (map[i][j]!=1&&map[i][j]!=0);
			if (map[i][j]){into[j]++;}
		}
	}
	
	dfs(0);
	printf("%ld",minans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值