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,那麼我們就可以記錄答案
- 如何優化?
- 首先,我們想到,如果兩點之間的距離都爲正,那麼我們就可以直接輸出2,因爲這兩個點就構成了最小的一個環
- 如果當前搜索的點答案已經到了3,那我我們直接輸出即可,因爲當前最小的環已經是最小的了
- 如果當前搜索到的路徑已經小於0,那麼我們就可以直接退出了,因爲如果真的有一個正環,那麼我們從哪個點開始掃都會是正值,因爲一個環會被掃到多次
- 再加上最優化搜索就差不多了,最慢的點只有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;
}