zoukankan      html  css  js  c++  java
  • bzoj 4773: 负环 floyd

    题目:

    对于边带权的有向图,找出一个点数最小的环,使得环上的边权和为负.
    2 <= n <= 300.

    题解:

    我们可以考虑从小到大枚举答案.
    然后每次枚举更大的答案的时候就从当前的较小的答案更新过去.
    更具体一点,可以设f[i][j]表示当前的步数下从i走到j的最短路.
    每次更新本质就是一个简单的动态规划的状态转移.
    但是这样复杂度是(O(n^4))的.
    肯定跑不过去.
    更近一步地,从刚才的思路转变一下.
    我们设(f[d][i][j])表示(i o j)(2^d)步时的最短路.
    我们可以在(O(n^3log n))的复杂度内预处理出来整个数组.
    震惊! (n^3log n)竟可以跑过300 !!!
    然后我们利用这个数组将上面的从小到大枚举答案变为类似二分的过程!
    也就是说我们可以枚举答案的二进制位.
    不难发现如果((010)_2)成立那么((100)_2)也成立,所以答案具有单调性.
    所以二分即可.

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    inline void read(int &x){
        x=0;static char ch;bool flag = false;
        while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
        while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
    }
    #define rg register int
    #define rep(i,a,b) for(rg i=(a);i<=(b);++i)
    #define per(i,a,b) for(rg i=(a);i>=(b);--i)
    const int maxn = 510;
    const int inf = 0x3f3f3f3f;
    int dis[9][maxn][maxn];
    int f[maxn][maxn],g[maxn][maxn];
    int main(){
        int n,m;read(n);read(m);
        int u,v,w;
        rep(i,1,n) rep(j,1,n) if(i != j) dis[0][i][j] = inf;
        rep(i,1,m){
            read(u);read(v);read(w);
            dis[0][u][v] = w;
        }
        rep(d,1,8){
            rep(i,1,n) rep(j,1,n){
                if(i != j) dis[d][i][j] = inf;
                else dis[d][i][j] = 0;
            }
            rep(k,1,n) rep(i,1,n) rep(j,1,n){
                dis[d][i][j] = min(dis[d-1][i][k]+dis[d-1][k][j],dis[d][i][j]);
            }
        }
        rep(i,1,n) rep(j,1,n){
            if(i != j) g[i][j] = inf;
            else g[i][j] = 0;
        }
        bool flag;
        int ans = 0;
        per(d,8,0){
            rep(i,1,n) rep(j,1,n) f[i][j] = g[i][j];
            rep(k,1,n) rep(i,1,n) rep(j,1,n){
                f[i][j] = min(f[i][j],min(g[i][k]+dis[d][k][j],dis[d][i][k]+g[k][j]));
            }
            flag = false;
            rep(i,1,n) if(f[i][i] < 0){
                flag = true;
                break;
            }
            if(flag) continue;
            rep(i,1,n) rep(j,1,n) g[i][j] = f[i][j];
            ans |= 1 << d;
        }
        if(++ans > n) puts("0");
        else printf("%d
    ",ans);    return 0;
    }
    
    
  • 相关阅读:
    Mono项目将继续推动基于Linux的开发
    VS.PHP 在Visual Studio 下的 PHP 开发 IDE 工具
    SQL Server 2008 的代码提示功能
    想做的时候没有机会了
    我的最爱
    双缓冲
    做个好男人!
    再见了,曾经喜欢过的歌手
    看看他是喜欢你还是爱你的~~~
    独家:未来五年程序员应当具备的十项技能
  • 原文地址:https://www.cnblogs.com/Skyminer/p/6770288.html
Copyright © 2011-2022 走看看