zoukankan      html  css  js  c++  java
  • Codeforces 147B Smile House(DP预处理 + 倍增)

    题目链接  Smile House

    题意  给定一个$n$个点的有向图,求一个点数最少的环,使得边权之和$>0$,这里的环可以重复经过点和边。

        满足  $n <= 300$

     

    首先答案肯定是单调的,但是观察发现只有当我们给所有的点加一个自环的时候才满足这个性质。

    考虑$DP$。设$f[i][j][k]$为长度为$i$,从$j$走到$k$能经过的最大边权和。

    那么$f[i][j][k] = min(f[i-1][j][l] + g[l][k])$,这样的预处理是$O(n^{4})$的,$TLE$。

    考虑$f[i][j][k]$为长度为$2^{i}$,从$j$走到$k$能经过的最大边权和。

    那么$f[i][j][k] = min(f[i-1][j][l] + f[i - 1][l][k])$, 这样的预处理是$O(n^{3}logn)$的。

     

    现在考虑二分答案。把当前验证的答案$u$拆成不同的几个$2$的幂次(最多$logn$个)。

    令$u = 2^{a_{1}} + 2^{a_{2}} + 2^{a_{3}} + ... + 2 ^ {a_{m}}$

    设$c[i][j]$为若只考虑长度为$2^{a_{1}}$, $2^{a_{2}}$, ...,  $2^{a_{m}}$的边,从$i$走到$j$经过的路径权值和的最大值。

    那么又是一波$O(n^{3}logn)$的转移。

    若最后存在$c[i][i] > 0$,则$u$可行。

    加上二分答案的那个$log$,

    时间复杂度$O(n^{3}log^{2}n)$

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define rep(i, a, b)	for (int i(a); i <= (b); ++i)
    #define dec(i, a, b)	for (int i(a); i >= (b); --i)
    #define MP		make_pair
    #define fi		first
    #define se		second
    
    
    typedef long long LL;
    
    const int inf = 1e9;
    
    const int N = 306;
    
    int n, m;
    int f[10][N][N], c[2][N][N];
    int l, r;
    
    bool check(int u){
    	int x = 0;
    	rep(i, 1, n) rep(j, 1, n) c[0][i][j] = -inf * (i != j);
    	rep(st, 0, 9) if (u & (1 << st)){
    		x ^= 1;
    		rep(i, 1, n) rep(j, 1, n) c[x][i][j] = -inf;
    		rep(k, 1, n) rep(i, 1, n) rep(j, 1, n)
    			c[x][i][j] = max(c[x][i][j], c[x ^ 1][i][k] + f[st][k][j]);
    	}
    
    	rep(i, 1, n) if (c[x][i][i] > 0) return true;
    	return false;
    }
    
    
    int main(){
    
    	scanf("%d%d", &n, &m);
    
    	rep(st, 0, 9) rep(i, 0, n + 1) rep(j, 0, n + 1) f[st][i][j] = -inf * (int)(i != j);
    	rep(i, 1, m){
    		int x, y;
    		scanf("%d%d", &x, &y);
    		scanf("%d%d", &f[0][x][y], &f[0][y][x]);
    	}
    
    	rep(st, 1, 9){
    		rep(k, 1, n){
    			rep(i, 1, n){
    				rep(j, 1, n){
    					f[st][i][j] = max(f[st][i][j], f[st - 1][i][k] + f[st - 1][k][j]);
    				}
    			}
    		}
    	}
    
    	l = 2, r = n;
    	if (!check(r)) return 0 * puts("0");
    	while (l + 1 < r){
    		int mid = (l + r) >> 1;
    		if (check(mid)) r = mid;
    		else l = mid + 1;
    	}
    
    	if (check(l)) printf("%d
    ", l);
    	else printf("%d
    ", r);
    	return 0;
    }
    

     

    但是还有更优的办法。

    我们可以联想到$O(logn)$求$LCA$时的做法。

    $2$的幂次从高到低依次判断,若当前的状态和已经存储的状态结合后可以形成正环,那么恰恰不能取这个幂次。

    类比求$LCA$的过程,当$x$往上跳$2^{i}$步后到达的结点和y往上跳$2^{i}$步后到达的结点一样,那么恰恰不能往上跳$2^{i}$步。

    我们可以类比这个方法求解这道题最后的答案。

    也就是不能形成正环的最大值$ans$

    别忘了最后$ans$得加$1$

    这样相对前一种方法,复杂度少了一个$log$

    时间复杂度$O(n^{3}logn)$

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define rep(i, a, b)	for (int i(a); i <= (b); ++i)
    #define dec(i, a, b)	for (int i(a); i >= (b); --i)
    
    const int inf = 1e9;
    
    const int N = 306;
    
    int n, m;
    int f[10][N][N], c[N][N], g[N][N];
    int l, r;
    int ans;
    
    int main(){
    
    	scanf("%d%d", &n, &m);
    
    	rep(st, 0, 9) rep(i, 0, n + 1) rep(j, 0, n + 1) f[st][i][j] = g[i][j] = -inf * (int)(i != j);
    	rep(i, 1, m){
    		int x, y;
    		scanf("%d%d", &x, &y);
    		scanf("%d%d", &f[0][x][y], &f[0][y][x]);
    	}
    
    	rep(st, 1, 9){
    		rep(k, 1, n){
    			rep(i, 1, n){
    				rep(j, 1, n){
    					f[st][i][j] = max(f[st][i][j], f[st - 1][i][k] + f[st - 1][k][j]);
    				}
    			}
    		}
    	}
    
    	ans = 0;
    	dec(st, 9, 0){
    		rep(i, 0, n + 1) rep(j, 0, n + 1) c[i][j] = -inf;
    		rep(k, 1, n) rep(i, 1, n) rep(j, 1, n) c[i][j] = max(c[i][j], g[i][k] + f[st][k][j]);
    		bool flag = false;
    		rep(i, 1, n) if (c[i][i] > 0){ flag = true; break;}
    		if (!flag){
    			ans += 1 << st;
    			rep(i, 0, n + 1) rep(j, 0, n + 1) g[i][j] = c[i][j];
    		}
    	}
    
    	++ans;
    	printf("%d
    ", ans > n ? 0 : ans);	
    	return 0;
    }
    

      

     

       

  • 相关阅读:
    java
    Java 自定义异常(转载)
    java中更新文件时,指定原文件的编码格式,防止编码格式不对,造成乱码
    tar命令压缩和解压
    微服务之服务注册与发现--Consul(转载)
    git push 时:报missing Change-Id in commit message footer的错误
    git 版本回退
    item 快捷键
    mac下mysql的卸载和安装
    JAVA正则表达式:Pattern类与Matcher类详解(转)
  • 原文地址:https://www.cnblogs.com/cxhscst2/p/8419870.html
Copyright © 2011-2022 走看看