HDU Summer Holiday

 

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <map>
#include <stack>
#include <algorithm>
//#include "myAlgorithm.h"

#define MAX 100005
#define OFFENCE (1e9 + 5)
#define INF (1e8 + 5)
#define eps 1e-9
#define Rep(s, e) for( int i = s; i <= e; i++)
#define Cep(e, s) for( int j = e; j >= s; j --)
#define PI acos(-1.0)
//#pragma comment(linker, "/STACK:36777216") ///传说中的外挂
using namespace std;
/**
    想了好久~, 先想到的是在强连通分量中, 如果有选的就选最小的~ , 就把他当成
    一个点值就是最小值(网上叫:强连通缩点)
    然后剩下的图就是(如果i - j, 就不可能存在j - i)的 n个独立集合(n >= 1)

    每个集合的求法自然一样,  对单个集合分析:
    这个集合有可能是树, 如果是树的话, 根节点的值就是答案, 因为树根是必须联系的, 而且
    联系树根之后就不必联系其他, 但是如果不是简单的树,   有可能是多颗树相交~~~,
    想到这里就跪了~~, 没想到只差一点了~

    如果图是多棵树相交, 那么, 入度为零的所有点就是树根,  联系所有树根就行了~~~
    联系的人数自然不能再少了, 就是最少人数, 必须叫的最少人的花费自然就是最少花费~~
    这个-->入度为零的点真的是非常有用的点~~~
    
    G++ TLE  C++ AC                                        
     
                                                                                                       ------学习了~~~

*/
int dfn[MAX], low[MAX], v[MAX];
int index, num;
stack<int>s;
int cost[MAX], into[MAX];
int n, m;
vector<vector<int> >adj;
int nodeSet[MAX];
int minNum, minCost;

void tarjan(int node){
    //cout<<node<<endl;
    v[node] = 1;
    s.push(node);
    dfn[node] = low[node] = ++index;
    int len  = adj[node].size();
    Rep(0, len - 1){
        int pos = adj[node][i];
        if(!dfn[pos]){
            tarjan(pos);
            low[node] = min(low[node], low[pos]);
        }
        else if(v[pos]){                                            ///,b了,v[node]
            low[node] = min(low[node], dfn[pos]);
        }
    }
    if(low[node] == dfn[node]){
        num++;
        while(!s.empty()){///获取的强连通分量
            int t = s.top();
            s.pop();
            v[t] = 0;
            nodeSet[t] = num;
            if(t == node)
                break;
        }
    }
}
void init(){
    adj.assign(n + 1, vector<int>() );
    memset(dfn, 0, sizeof(dfn));
    memset(v, 0, sizeof(v));
    memset(into, 0, sizeof(into));
    memset(nodeSet, 0, sizeof(nodeSet));
    index = num = 0;
    minCost = minNum = 0;
    while(!s.empty()){
        s.pop();
    }
}
int main(){
    while(cin>>n>>m){
        int a, b;
        init();
        Rep(1, n)cin>>cost[i];
        Rep(0, m - 1){
            cin>>a>>b;
            adj[a].push_back(b);
        }/// end input
        Rep(1, n){
            if(!dfn[i]){
                tarjan(i);
            }
        }
        //cout<<endl;
//        cout<<num<<endl;
//        Rep(1, num){
//            for(int j = 1; j <= n; j++){
//                if(nodeSet[j] == i)cout<<j<<" ";
//            }cout<<endl;
//        }
        Rep(1, n){
            int len = adj[i].size();
            for(int j = 0; j < len; j++){
                int pos = adj[i][j];
                if(nodeSet[pos] != nodeSet[i]){///不在同一个强连通分量上则记录分量入度
                    into[nodeSet[pos]]++;
                }
            }
        }

        Rep(1, num){
            if(!into[i]){
                minNum++;
                int minCostt = INF;
                for(int j = 1; j <= n; j++){///强连通分量中取最小
                    if(nodeSet[j] == i){
                        minCostt = min(minCostt, cost[j]);
                    }
                }
                minCost += minCostt;
            }
        }
        cout<<minNum<<" "<<minCost<<endl;
    }
    return 0;
}


 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值