BZOJ3763

3763: 最小环

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 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

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");
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值