【BZOJ 2260&&4349】最小树形图&&商店购物 朱刘算法

236 篇文章 0 订阅
1 篇文章 0 订阅

一天到晚没事干天天想着炸机房。。。。

我:这道题朱刘算法裸题。。

cja:什么,猪牛算法,什么鬼。。。莫非是诸葛亮和刘备发明的

我:。。。。。。。


#include<cstdio>  
#include<cstring>  
#include<iostream>  
#define maxn 10021   
using namespace std;  
int n,m,times[maxn],nu[maxn],tot,head[maxn],pre[maxn],vis[maxn],id[maxn];  
double cost[maxn],in[maxn],Min[maxn];  
struct edge{int a,b;double w;}e[maxn];  
void adde(int a,int b,double c){  
    e[++tot].a=a,e[tot].b=b,e[tot].w=c;  
}  
double solve(){  
    double ans=0;int rt=n;  
    while(true){  
        for(int i=1;i<=n;i++)in[i]=1e9;//找对于每一个点来说最小的入边 
        for(int a,b,i=1;i<=tot;i++){  
            a=e[i].a,b=e[i].b;  
            if(a!=b&&e[i].w<in[b])in[b]=e[i].w,pre[b]=a; //更新每一个点的最小的入边以及这条入边是从哪一个点过来的 
        }in[rt]=0;  //根的入边没有 
        int cnt=0;  
        for(int i=1;i<=n;i++)id[i]=vis[i]=0; //清空标号和懒惰标记 
        for(int i=1;i<=n;i++){  
            ans+=in[i]; //答案先加上这每一个点的入边,但是这样以后就会产生很多的环 
            int j;  
            //找到i这个点所在的环,如果找到了根节点的话就直接停止,因为已经没有环了
			//如果找到的节点的编号也是i的话说明这个环已经找完了
			//如果下一个节点已经在一个环内了,说明当前节点之前的是一条链然后上面连着 
            for(j=i; vis[j]!=i && !id[j] && j!=rt;j=pre[j])vis[j]=i;  
            //如果j是已经有标号了或者说j就是根的话就说明i之前的并不是对环 
            if(!id[j]&&j!=rt){  
                id[j]=++cnt;   
                //然后再来一个循环 
                for(int k=pre[j];k!=j;k=pre[k])  
                    id[k]=cnt;  
            }  
        }  
        //说明已经没有环了 
        if(cnt==0)return ans; 
		//每一单独的点且不在环中就当做一个单独的单点的环处理 
		//也就是说在这里重新标号,对于一个环的环就当做一个新的节点然后删除最大的一条边 
        for(int i=1;i<=n;i++)if(!id[i])id[i]=++cnt;  
        //枚举每一条边 
        for(int i=1;i<=tot;i++){  
            double v=in[e[i].b];//边权 
            //如果这个点在后面还会被选上那么就表明之前的边并不会被选上
			//所以新的边权要剪掉之前选上的边权 
            e[i].a=id[e[i].a],e[i].b=id[e[i].b];  
            if(e[i].a!=e[i].b)e[i].w-=v;  
        }  
        n=cnt,rt=id[rt];  
    }  
}  
int main(){  
    scanf("%d",&n);int N=n;  
    for(int i=1;i<=n;i++){  
        scanf("%lf%d",cost+i,times+i);  
        if(times[i])adde(n+1,tot+1,cost[i]),Min[i]=cost[i],nu[i]=tot;  
    }n=tot+1;  
    for(int i=1;i<=tot;i++)e[i].a=n;  
    scanf("%d",&m);int a,b;double c;  
    for(int i=1;i<=m;i++){  
        scanf("%d%d%lf",&a,&b,&c);  
        if(times[a]&×[b]){  
            Min[b]=min(Min[b],c);  
            adde(nu[a],nu[b],c);  
        }  
    }  
    double ans=0;  
    ans+=solve();  
    for(int i=1;i<=N;i++){  
        if(times[i])ans+=(times[i]-1)*Min[i];  
    }  
    printf("%.2lf",ans);  
    return 0;  
}  


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值