hdu1827Summer Holiday(强连通分量-最小权点基)

本文介绍了一道经典的图论问题——最小权点基问题,通过一个具体的编程竞赛题目进行阐述。文章详细解释了如何利用Tarjan算法找到强连通分量,并在此基础上解决有向图中的最小权点选择问题,最终实现高效的通知方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Summer Holiday

Time Limit: 10000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4111    Accepted Submission(s): 1853


Problem Description
To see a World in a Grain of Sand 
And a Heaven in a Wild Flower, 
Hold Infinity in the palm of your hand 
And Eternity in an hour. 
                  —— William Blake

听说lcy帮大家预定了新马泰7日游,Wiskey真是高兴的夜不能寐啊,他想着得快点把这消息告诉大家,虽然他手上有所有人的联系方式,但是一个一个联系过去实在太耗时间和电话费了。他知道其他人也有一些别人的联系方式,这样他可以通知其他人,再让其他人帮忙通知一下别人。你能帮Wiskey计算出至少要通知多少人,至少得花多少电话费就能让所有人都被通知到吗?
 

Input
多组测试数组,以EOF结束。
第一行两个整数N和M(1<=N<=1000, 1<=M<=2000),表示人数和联系对数。
接下一行有N个整数,表示Wiskey联系第i个人的电话费用。
接着有M行,每行有两个整数X,Y,表示X能联系到Y,但是不表示Y也能联系X。
 

Output
输出最小联系人数和最小花费。
每个CASE输出答案一行。
 

Sample Input
  
12 16 2 2 2 2 2 2 2 2 2 2 2 2 1 3 3 2 2 1 3 4 2 4 3 5 5 4 4 6 6 4 7 4 7 12 7 8 8 7 8 9 10 9 11 10
 

Sample Output
  
3 6
 

Author
威士忌
 

Source
 

Recommend
威士忌   |   We have carefully selected several similar problems for you:   1823  1824  1826  1822  1825 


思路:最小权点基模板题,首先求出所有的强连通分量,把所有分量缩成一个点,于是该图变成了有向无环图,接下来找出所有强连通分量的最小权点,如果该连通分量入度为0,直接加上此连通分量的最小权点的权值。注意该题输入数据量较大,不能使用c++的输入流,否则会超时。。。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<stack>
#include<cstdio>
#include<climits>
using namespace std;
#define inf INT_MAX
const int mx = 1010;
int dfn[mx],low[mx],vis[mx],num[mx],dg[mx],pay[mx],mi[mx];
int n,m,id,cnt,ans;
vector<int> mp[mx];
stack<int> s;

void init(){
    cnt=0;
    id=0;
    ans=0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(num,0,sizeof(num));
    memset(dg,0,sizeof(dg));
    memset(mi,0,sizeof(mi));
    for(int i=1;i<=n;i++)
        mp[i].clear();
    while(!s.empty())
        s.pop();
}

void tarjan(int x){
    dfn[x]=low[x]=++id;
    s.push(x);
    vis[x]=1;
    for(int i=0;i<mp[x].size();i++){
        int t=mp[x][i];
        if(!dfn[t]) {
            tarjan(t);
            low[x]=min(low[x],low[t]);
        }else if(vis[t]) low[x]=min(low[x],dfn[t]);
    }
    if(dfn[x]==low[x]){
        int tp,tt=inf;
        cnt++;
        do{
            tp=s.top();
            vis[tp]=0;
            num[tp]=cnt;
            if(pay[tp]<tt)
                tt=pay[tp];
            ///tt=min(tt,pay[tp]);
            s.pop();
        }while(tp!=x);
        mi[cnt]=tt;
    }
}

void solve(){
    int sum=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<mp[i].size();j++)
            if(num[i]!=num[mp[i][j]]) dg[num[mp[i][j]]]++;

    for(int i=1;i<=cnt;i++){
        if(!dg[i]) {
            sum++;
            ans+=mi[i];
        }
    }
    printf("%d %d\n",sum,ans);
    ///cout<<sum<<" "<<ans<<endl;
}

int main(){
    while(~scanf("%d%d",&n,&m)){
        init();
        for(int i=1;i<=n;i++)
            scanf("%d",&pay[i]);
        for(int i=0;i<m;i++){
            int a,b;
            ///cin>>a>>b;果断超时
            scanf("%d%d",&a,&b);
            mp[a].push_back(b);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i]) tarjan(i);
        solve();
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值