BZOJ 2878 Noi2012 迷失游乐园

2 篇文章 0 订阅

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

代码后详细说明:

#include <bits/stdc++.h>
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<edge> 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;i<g[x].size();i++)
    {
        edge& e = g[x][i];
        if(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;i<g[x].size();i++) if(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) 的。但此题数据无心卡菊花图,此算法的运行时间良好。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值