3763: 最小环
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 71 Solved: 22
[ Submit][ Status][ Discuss]
Description
给定一个无向图,某些点之间连有一条可以双向通过的边,假设结点a,b之间有边,则从a到b会得到c[a][b]个糖果,从b到a会得到c[b][a]个糖果。
问在图中是否存在一个环,可以无限获得糖果(即边权和为正);如果存在,在这些正环中,点数最少的环有多少个点?
对于环的定义:环是一些点的序列,a1,a2,...,ak,a1和a2相连,a2和a3相连,...,ak和a1相连。其中ai和aj可以重合。对于这个环,它的点数视为K。
Input
第一行包含两个整数N,M,分别表示点数和边数。
接下来M行,每行i,j,c[i][j],c[j][i],表示i号点和j号点有一条边,从i到j获得c[i][j]个糖果,从j到i获得c[j][i]个糖果。
Output
输出只包含一个整数,表示能够无限获得糖果的环中,最少的点数。
如果不存在那样的环,输出0。
Sample Input
4 4
1 2 -10 3
1 3 1 -10
2 4 -10 -1
3 4 0 -3
1 2 -10 3
1 3 1 -10
2 4 -10 -1
3 4 0 -3
Sample Output
4
HINT
对于100%的数据,1<=N<=300,0<=M<=N*(N-1)/2,-10000<=c[i][j]<=10000。
数据不存在重边和自环。
其实这题是CF的147B,现在只能去那里交了
二分答案k,先预处理走2^t步时的最短路,然后类似于快速幂的去算走k步时的最大环,最大环即max{dp[k][i][i]}
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define inf (1<<24)
using namespace std;
int dp[10][310][310],s[310][310],px[310][310],n,m;
bool check(int k){
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
s[i][j]=inf;
memset(px,0x3f,sizeof(px));
for(int p=0;k>>p;p++)if((k>>p)&1){
for(int z=1;z<=n;++z)for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)
px[i][j]=min(px[i][j],min(s[i][z]+dp[p][z][j],dp[p][i][z]+s[z][j]));
for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)s[i][j]=min(s[i][j],min(px[i][j],dp[p][i][j]));
memset(px,0x3f,sizeof(px));
}
for(int i=1;i<=n;++i)if(s[i][i]<0)return true;
return false;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
for(int k=0;(n<<1)>>k;++k)
dp[k][i][j]=inf;
for(int i=1,u,v,a,b;i<=m;++i)
scanf("%d%d",&u,&v),
scanf("%d%d",&a,&b),
dp[0][u][v]=min(dp[0][u][v],-a),
dp[0][v][u]=min(dp[0][v][u],-b);
for(int p=1;(n<<1)>>p;++p)for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)
dp[p][i][j]=min(dp[p-1][i][k]+dp[p-1][k][j],dp[p][i][j]);
int l=1,r=n;
// printf("[%d]",check(7));
while(l<r){
int mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
if(check(l))printf("%d",l);
else printf("0");
}