[HDU3072]:Intelligence System(塔尖+贪心)

题目传送门


题目描述

“这一切都是命运石之门的选择。”
试图研制时间机器的机关SERN截获了中二科学家伦太郎发往过去的一条短 信,并由此得知了伦太郎制作出了电话微波炉(仮)。
为了掌握时间机器的技术,SERN总部必须尽快将这个消息通过地下秘密通讯 网络,传达到所有分部。
SERN共有N个部门(总部编号为0),通讯网络有M条单向通讯线路,每条线 路有一个固定的通讯花费$C_i$
为了保密,消息的传递只能按照固定的方式进行:从一个已知消息的部门向 另一个与它有线路的部门传递(可能存在多条通信线路)。我们定义总费用为所 有部门传递消息的费用和。
幸运的是,如果两个部门可以直接或间接地相互传递消息(即能按照上述方法将信息由X传递到Y,同时能由Y传递到X),我们就可以忽略它们之间的花费。
由于资金问题(预算都花在粒子对撞机上了),SERN总部的工程师希望知道, 达到目标的最小花费是多少。


输入格式

多组数据,文件以20结尾。
每组数据第一行,一个整数N,表示有N个包括总部的部门(从0开始编号)。 然后是一个整数M,表示有M条单向通讯线路。
接下来M行,每行三个整数$X_i$,$Y_i$,$C_i$,表示第i条线路从$X_i$连向$Y_i$,花费为$C_i$


输出格式

每组数据一行,一个整数表示达到目标的最小花费。


样例

样例输入:

3 3
0 1 100
1 2 50
0 2 100
3 3
0 1 100
1 2 50
2 1 100
2 2
0 1 50
0 1 100
0 0

样例输出:

150
100
50


数据范围与提示

样例解释:
第一组数据:总部把消息传给分部1,分部1再传给分部2。总费用:100+50=150
第二组数据:总部把消息传给分部1,由于分部1和分部2可以互相传递消息,所以分部1可以无费用把消息传给2。总费用:100+0=100
第三组数据:总部把消息传给分部1,最小费用为50。总费用:50
数据范围:
对于10%的数据,保证M=N-1
对于另30%的数据,N≤20M≤20
对于100%的数据,N≤50000M≤105$C_i$≤105,数据组数≤5
数据保证一定可以将信息传递到所有部门。


题解

不知道你有没有注意到这句话:

  幸运的是,如果两个部门可以直接或间接地相互传递消息(即能按照上述方法将信息由X传递到Y,同时能由Y传递到X),我们就可以忽略它们之间的花费。

这意味着什么呢?

就是说,在一个强联通分量里,任意两点之间相互到达的花费为0,那么直接一个塔尖缩点刚上去就好了。

那么缩完点之后要怎么计算答案呢?

考虑这样一个问题,贪心思想,我们要从根节点到达所有点,每一个点被到达一次即可,那么我们就找所有能到达这个点中权值最小的那个边,走它就好了。

枚举所有的边,找非强联通分量里的边,不断更新这条边的to的最小值即可。

模板一定要打对!!!


代码时刻

本题代码:

#include<bits/stdc++.h>
using namespace std;
struct rec
{
	int nxt;
	int to;
	int w;
}e[100010];//存边
int n,m;
int head[50010],cnt;
int dfn[50010],low[50010],sta[50010],ins[50010],c[50010],num,top,tot;//塔尖
int ans;//存储答案
int fl[50010];//存储最小的那个边
void pre_work()//多测不清空,爆零两行泪TAT……
{
	cnt=num=top=tot=0;
	ans=0;
	for(int i=1;i<=n;i++)
		head[i]=dfn[i]=low[i]=ins[i]=c[i]=0;
	memset(fl,0x3f,sizeof(fl));
}
void add(int x,int y,int w)//建边
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	e[cnt].w=w;
	head[x]=cnt;
}
void tarjan(int x)//塔尖求强联通分量
{
	dfn[x]=low[x]=++num;
	sta[++top]=x;
	ins[x]=1;
	for(int i=head[x];i;i=e[i].nxt)
	{
		if(!dfn[e[i].to])
		{
			tarjan(e[i].to);
			low[x]=min(low[x],low[e[i].to]);
		}
		else if(ins[e[i].to])
			low[x]=min(low[x],dfn[e[i].to]);
	}
	if(dfn[x]==low[x])
	{
		tot++;
		int y;
		do
		{
			y=sta[top--];
			ins[y]=0;
			c[y]=tot;
		}while(x!=y);
	}
}
int main()
{
	memset(fl,0x3f,sizeof(fl));
	while(1)
	{
		scanf("%d%d",&n,&m);
		if(!n&&!m)break;
		for(int i=1;i<=m;i++)
		{
			int x,y,c;
			scanf("%d%d%d",&x,&y,&c);
			add(x+1,y+1,c);
		}
		tarjan(1);
		for(int x=1;x<=n;x++)
			for(int i=head[x];i;i=e[i].nxt)
				if(c[x]!=c[e[i].to])
					fl[c[e[i].to]]=min(fl[c[e[i].to]],e[i].w);//不断更新最小值
		fl[c[1]]=0;
		for(int i=1;i<=tot;i++)ans+=fl[i];
		printf("%d\n",ans);
		pre_work();
	}
	return 0;
}

HDU代码(输入格式不同):

#include<bits/stdc++.h>
using namespace std;
struct rec
{
    int nxt;
    int to;
    int w;
}e[100010];
int n,m;
int head[50010],cnt;
int dfn[50010],low[50010],sta[50010],ins[50010],c[50010],num,top,tot;
int ans;
int fl[50010];
void pre_work()
{
    cnt=num=top=tot=0;
    ans=0;
    for(int i=1;i<=n;i++)
        head[i]=dfn[i]=low[i]=ins[i]=c[i]=0;
    memset(fl,0x3f,sizeof(fl));
}
void add(int x,int y,int w)
{
    e[++cnt].nxt=head[x];
    e[cnt].to=y;
    e[cnt].w=w;
    head[x]=cnt;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++num;
    sta[++top]=x;
    ins[x]=1;
    for(int i=head[x];i;i=e[i].nxt)
    {
        if(!dfn[e[i].to])
        {
            tarjan(e[i].to);
            low[x]=min(low[x],low[e[i].to]);
        }
        else if(ins[e[i].to])
            low[x]=min(low[x],dfn[e[i].to]);
    }
    if(dfn[x]==low[x])
    {
        tot++;
        int y;
        do
        {
            y=sta[top--];
            ins[y]=0;
            c[y]=tot;
        }while(x!=y);
    }
}
int main()
{
    memset(fl,0x3f,sizeof(fl));
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=m;i++)
        {
            int x,y,c;
            scanf("%d%d%d",&x,&y,&c);
            add(x+1,y+1,c);
        }
        tarjan(1);
        for(int x=1;x<=n;x++)
            for(int i=head[x];i;i=e[i].nxt)
                if(c[x]!=c[e[i].to])
                    fl[c[e[i].to]]=min(fl[c[e[i].to]],e[i].w);
        fl[c[1]]=0;
        for(int i=1;i<=tot;i++)ans+=fl[i];
        printf("%d\n",ans);
        pre_work();
    }
    return 0;
}

rp++

转载于:https://www.cnblogs.com/wzc521/p/11196134.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值