BZOJ 4349 最小树形图

最小树形图+朱刘算法

朱刘算法:http://acm.nudt.edu.cn/~twcourse/Tree.html#a17 啊虽然是繁体的不过写的挺详细的, 还有啊那个不叫水母叫环套树......

为什么网上的朱刘算法都没有正确性证明啊,好气噢……感性理解一下吧

这题的话,显然先把全部的都打一遍再把每一个打完,不会比其他方法更劣,因为其他方法通过交换打的过程都能变成前者。那就变成我们要找到最小的代价能把每一个都打一遍,显然是一个最小树形图问题,套朱刘算法即可,本题不会无解。

交代码的时候遇到恶意卡评测,一堆代码在Pending,我该说什么好。(截至写完这一篇,已经卡了一页半了,不知道之后会怎样)

看到了一些心碎的事情,学姐R.I.P.

#include<cstdio>
#include<algorithm>
#define N 55
#define K 23333
using namespace std;
namespace runzhe2000
{
    typedef double db;
    const double INF = 1e9;
    struct pdge{int from, to; db val;}pe[K];
    int pecnt, n, k, b[N], S, pre[N], vis[N], bel[N], ban[N];
    db a[N], ans, in[N];
    db DMST()
    {
        db r = 0;
        for(;;)
        {
            for(int i = 1; i <= n; i++) in[i] = INF, pre[i] = vis[i] = bel[i] = 0;
            for(int i = 1; i <= pecnt; i++)
            {
                int from = pe[i].from, to = pe[i].to;
                if(from != to && pe[i].val < in[to])
                    in[to] = pe[i].val, pre[to] = from;
            }
            int findring = 0;
            for(int i = 1, j; i <= n; i++) if(!ban[i])
            {
                r += in[i];
                for(j = i; !vis[j] && j; j = pre[j]) vis[j] = i;
                if(vis[j] == i)
                {
                    for(int k = pre[j]; k != j; k = pre[k]) bel[k] = j;
                    bel[j] = j; findring = 1;
                }
                if(!bel[i]) bel[i] = i;
            }
            for(int i = 1; i <= n; i++) if(!ban[i] && bel[i] != i) ban[i] = 1;
            if(!findring) return r;
            for(int i = 1; i <= pecnt; i++)
            {
                int &from = pe[i].from, &to = pe[i].to; db &val = pe[i].val;
                if(from != to)
                {
                    val -= in[to];
                    from = bel[from];
                    to = bel[to];
                }
            }
        }
        return -233;
    }
    void main()
    {
        scanf("%d",&n); S = n + 1;
        for(int i = 1; i <= n; i++)
        {
            scanf("%lf%d",&a[i],&b[i]);
            pe[++pecnt] = (pdge){S, i, a[i]};
        }
        scanf("%d",&k);
        for(int i = 1; i <= k; i++)
        {
            int x, y; db z;
            scanf("%d%d%lf",&x,&y,&z);
            pe[++pecnt] = (pdge){x, y, z};
            a[y] = min(a[y], z);
        }
        ans += DMST();
        for(int i = 1; i <= n; i++)
            ans += (b[i] - 1) * a[i];
        printf("%.2lf\n",ans);
    }
}
int main()
{
    runzhe2000::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值