【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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章