[Codeforces 147B Smile House] DP+倍增+二分

来源:互联网 发布:网络舆论的传播分析 编辑:程序博客网 时间:2024/06/08 17:27

[Codeforces 147B Smile House] DP+倍增+二分

1. 题目链接

[Codeforces 147B Smile House]

2. 题意描述

给定顶点数为n,边数m的有向图,求最小的正权环的大小。(最小指的环中顶点数最少)。
(1n300,0mn(n1)2,104aij104).

3. 解题思路

首先对无向图进行如下预处理:

  1. 给每个顶点添加一条边(自环),指向自己;
  2. 对于原图没有指定的边,我们统统认为它的权值为.

这样处理之后,我们就可以对答案进行二分了。
很容易想到,比较朴素的DP是用dp[s][i][j]表示从ij经过s条边的最大权值和。这样复杂度是O(n4)
采用倍增的思想,令dp[s][i][j]表示从ij经过2s条边的最大权值和。跟LCA类似,这个数组计算的复杂度为O(n3logn)
然后对答案进行二分,如何判断经过step条边是否能达到权值之和>0?。
首先,将step二进制展开。(如step=5=1+4,那么肯定是先走1条边然后走4条边)。然后同样的O(n3logn)时间内,利用dp[][][]数组拼凑起来得到ans[step][i][j](表示从ij经过step条边的最大权值和)。最后只需要判断ans[step][i][i]>0即可。 因为条件的转移只跟上一个状态有关,可以使用滚动数组。

4. 实现代码

#include <bits/stdc++.h>using namespace std;const int MAXN = 300 + 5;const int MBIT = 15 + 5;const int INF = 0x3f3f3f3f;int n, m, MT, dp[MBIT][MAXN][MAXN];int ans[2][MAXN][MAXN];inline void umax(int& a, const int &b) { a = max(a, b); }void init() {    MT = (int)floor(log2((long double)n));    for(int s = 0; s <= MT; s++) {        for(int i = 1; i <= n; i++) {            for(int j = 1; j <= n; j++) {                dp[s][i][j] = -INF;            }            dp[s][i][i] = 0;        }    }}void clr(int z) {    for(int i = 1; i <= n; i++) {        for(int j = 1; j <= n; j++) {            ans[z][i][j] = -INF;        }        ans[z][i][i] = 0;    }}bool check(int step) {    int z = 0;    clr(z);    for(int s = 0; s <= MT; s++) {        if(((step >> s) & 1) == 0) continue;        z ^= 1;        clr(z);        for(int k = 1; k <= n; k++) {            for(int i = 1; i <= n; i++) {                for(int j = 1; j <= n; j++) {                    umax(ans[z][i][j], ans[z ^ 1][i][k] + dp[s][k][j]);                }            }        }    }    for(int i = 1; i <= n; i++) {        if(ans[z][i][i] > 0) return true;    }    return false;}int main() {#ifndef ONLINE_JUDGE    freopen("input.txt", "r", stdin);#endif // ONLINE_JUDGE    int u, v, a, b;    while(~scanf("%d %d", &n, &m)) {        init();        for(int i = 1; i <= m; i++) {            scanf("%d %d %d %d", &u, &v, &a, &b);            dp[0][u][v] = a, dp[0][v][u] = b;        }        for(int s = 1; s <= MT; s++) {            for(int k = 1; k <= n; k++) {                for(int i = 1; i <= n; i++) {                    if(dp[s - 1][i][k] == -INF) continue;                    for(int j = 1; j <= n; j++) {                        umax(dp[s][i][j], dp[s - 1][i][k] + dp[s - 1][k][j]);                    }                }            }        }        int lb = 2, ub = n + 1, mid;        while(lb <= ub) {            mid = (lb + ub) >> 1;            if(check(mid))  ub = mid - 1;            else lb = mid + 1;        }        if(lb >= n + 1) lb = 0;        printf("%d\n", lb);    }    return 0;}
1 0
原创粉丝点击