BZOJ 2878 Noi2012 迷失游乐园

提示:
1. 先考虑暴力情况的 DP 方程
2. 环上的点很少, 而且如果你在环上走只能是连续的一段,也许可以改进一下 DP

代码后详细说明:

#include 
using namespace std;
const int maxn = 1e5+1e2;

int n , m;
struct edge { int t , v , re; edge(int t=0 , int v=0 , int re=0):t(t),v(v),re(re){} };

vector g[maxn];
vector<vector<double> > d[maxn];

bool vis[maxn] , inCircle[maxn]; 
int pre[maxn] , cnt , dic[maxn] , redic[maxn];;
bool dfs(int x)
{
    vis[x] = 1;
    for(int i=0;iif(e.t == pre[x]) continue;

        if(vis[e.t])
        {
            while(true)
            {
                dic[++cnt] = x;
                redic[x] = cnt;
                inCircle[x] = 1;
                if(x == e.t) break;
                x = pre[x];
            }
            return true;
        }
        pre[e.t] = x;
        if(dfs(e.t)) return true;
    }
    return false;
}

double dp(int x , int n1 , int n2)
{
    double &res = d[x][n1][n2];
    if(res > -1) return res;

    res = 0;
    int cnt = 0;
    for(int i=0;iif(i != n1)
    {
        edge& e = g[x][i];
        if(e.t == dic[n2]) continue;

        ++cnt;
        res += e.v;
        if(inCircle[x] == inCircle[e.t]) res += dp(e.t , e.re , n2);
        else if(inCircle[x] && !inCircle[e.t]) res += dp(e.t , e.re , 0);
        else if(!inCircle[x] && inCircle[e.t]) res += dp(e.t , e.re , redic[e.t]);
    }

    if(!cnt) return 0;
    return res /= cnt;
}

int main(int argc, char *argv[]) {
    #ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    #endif

    cin>>n>>m;
    for(int i=1 , s , t , v;i<=m;i++)
    {
        scanf("%d%d%d" ,&s , &t , &v);
        assert(s != t);
        g[s].push_back(edge(t , v , g[t].size()));
        g[t].push_back(edge(s , v , g[s].size()-1));
    }

    dfs(1);

    for(int i=1;i<=n;i++)
    {
        d[i].resize(g[i].size() + 1);
        for(int j=0;j<=g[i].size();j++) 
        {
            d[i][j].resize(cnt + 1);
            for(int k=0;k<=cnt;k++) d[i][j][k] = -100;
        }
    }

    double res = 0;
    for(int i=1;i<=n;i++) if(inCircle[i]) res += dp(i , g[i].size() , redic[i]); else res += dp(i , g[i].size() , 0);

    res /= n;
    printf("%.5lf\n" , res);
    return 0;
}

DP 方程:
di,j,k i j k

如果我们只考虑树上的情况,那么前两个状态就 Okay 了,由于此时可能有环,我们需要纪录每条路径第一次进入环的那个节点是谁,因为我们不能回到它。

一个优化的策略是在走出环之后强制第三维是 0

但细心的读者会发现这是个有可能超时的做法, 因为在菊花图中有于转移的时间复杂度是 O() 所以会退化成 O(n2) 的。但此题数据无心卡菊花图,此算法的运行时间良好。

你可能感兴趣的:(dp,基环)