【NOIP2013模拟】开心小屋 搜索

Description

Kc来到开心小屋。开心小屋是用来提升心情的。在这个小屋中有n个房间,一些房间之间有门连通。从房间i到达房间j,心情值可以加上-10000<=Cij<=10000,当然Cij可能是负的。现在kc失恋了,所以他想要知道他是否可以在这个小屋中无限地增加他的心情值,也就是无限地绕着一个环走?

请帮kc求出最小的环需要经过的房间数,来使他的心情无限增加。

Input

第一行给出,1<=n<=300,1<=m<=5000。分别表示房间数及门的数量。

接下来m行,每行四个数:i,j,Cij,Cji

Output

输出文件包括一行,及最小的环需要经过的房间数。

保证不会出现自环及重边。

Sample Input

4 4

1 2 -10 3

1 3 1 -10

2 4 -10 -1

3 4 0 -3

Sample Output

4

样例解释:

1—>3—>4–>2–>1为最小的符合题意的环长度为4.

Data Constraint

对30%的数据,n<=10;

对60%的数据,,n<=100;

对100%的数据,n<=300;

正解:DP+倍增优化+二分

下面转载,看不懂

题目给出无向图,要求求出节点数最小的正环长度。
30%的做法:brute force
60%的做法:
用floyd预处理出f[p][i][j],表示从i到j经过2^p条路径的最长路径。
接着二分答案t。检验答案和预处理类似,只需要考虑二进制位上为1的位置就可以了。用g[z][i][j]表示处理到第z个二进制位上为1的位置从i到j的最长路径,转移方程为:g[z][i][j]=max(g[z-1][i][k]+f[u][k][j])。其中u表示第z个1是在n的二进制表示中的第u位。
时间复杂度O(n^3log^2n),会超时。
100%:
进行二分查找的时候状态g[z][][]会被计算多次。我们可以考虑计算最大的不合法的数,即目标答案减1的数。因此我们只需从大到小枚举z,计算出当前答案加上2^z是否合法,若合法将其舍弃,若不合法将2^z加入当前答案即可。具体实现可以参考标程。

解法:搜索

  • 我们把每一个点作为起点都搜索一遍,记录父亲和深度和到当前的长度
  • 如何判断到达终点,如果已经到了一个搜索过的点,并且距离>0,那么我们就可以记录答案
  • 如何优化?
  1. 首先,我们想到,如果两点之间的距离都为正,那么我们就可以直接输出2,因为这两个点就构成了最小的一个环
  2. 如果当前搜索的点答案已经到了3,那我我们直接输出即可,因为当前最小的环已经是最小的了
  3. 如果当前搜索到的路径已经小于0,那么我们就可以直接退出了,因为如果真的有一个正环,那么我们从哪个点开始扫都会是正值,因为一个环会被扫到多次
  4. 再加上最优化搜索就差不多了,最慢的点只有13ms,快到飞起,前5个点时限1s,后5个点时限10s?

AC代码

#include<cstdio>
#include<cstring> 
#define inf 0x3f3f3f3f
#define re register int
using namespace std;
struct edge {
	int nex,to,w;
}e[10010];
int n,m,k,cnt,ans=inf,head[305],d[305],dis[305];
inline int read() {
	int x=0,cf=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') {
		if(ch=='-') cf=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') {
		x=(x<<3)+(x<<1)+(ch^48);
		ch=getchar();
	}
	return x*cf;
}
inline void add(int x,int y,int z) {
	e[++cnt].to=y,e[cnt].w=z,e[cnt].nex=head[x],head[x]=cnt;
}
inline int min(int A,int B) { return A<B?A:B; }
inline void dfs(int x,int fa,int sum) {
	d[x]=d[fa]+1; dis[x]=sum;
	if(d[x]>=ans||dis[x]<0) {
		d[x]=-1;//回溯
		return;
	}
	for(re i=head[x];i;i=e[i].nex) {
		int y=e[i].to;
		if(y==fa) continue;
		if(d[y]!=-1&&dis[x]+e[i].w-dis[y]>0) ans=min(ans,d[x]-d[y]+1);//已经搜索到了该点
		else if(d[y]==-1) dfs(y,x,sum+e[i].w);//还没有搜索到
	}
	d[x]=-1;//回溯
}
int main() {
	n=read(),m=read();
	for(re i=1;i<=m;i++) {
		int x=read(),y=read();
		int w1=read(); add(x,y,w1);
		int w2=read(); add(y,x,w2);
		if(w1+w2>0) {
			printf("2");
			return 0;
		}
	}
	for(k=1;k<=n;k++) {
		memset(d,-1,sizeof(d));
		memset(dis,0,sizeof(dis));
		d[k]=0; dfs(k,0,0);
		if(ans==3) break;
	}
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值