【题解】Summer Holiday HDU - 1827 ⭐⭐⭐ 【强连通 缩点】

Summer Holiday HDU - 1827

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

Output

Examples

Hint




题意:

题解:

首先使用Korasaju求出SCC的拓扑序, 将其升序排列, 然后每取一个点就Dfs标记一下可达的所有点, 同时记录选取的SCC, 即是最小选取点数.
最后遍历选取的SCC, 在该SCC的所有点中找到费用最低的点, 累加即是最低费用.

当然这道题也可以使用Tarjan, 求出入度为0的点数目, 然后再在对应的SCC中重复上述步骤

经验小结:


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef  long long LL;
const int inf = 1<<30;
const LL maxn = 1010;

int N, M;
vector<int> G[maxn];
vector<int> rG[maxn];
vector<int> vs;
bool used[maxn];
int topo[maxn];
int f[maxn];    //费用
typedef pair<int, int> P; //每个SCC对应的拓扑序依次排列
priority_queue<P, vector<P>, greater<P> > q;
vector<int> mark;

void Dfs(int u){
    used[u] = true;
    for(int v = 0; v < G[u].size(); ++v)
        if(!used[G[u][v]])
            Dfs(G[u][v]);
    vs.push_back(u);
}
void rDfs(int u, int k){
    used[u] = true;
    topo[u] = k;
    q.push(P(k, u));    //拓扑序入队
    for(int v = 0; v < rG[u].size(); ++v)
        if(!used[rG[u][v]])
            rDfs(rG[u][v], k);
}
int scc(){
    ms(used, 0);
    for(int i = 1; i <= N; ++i)
        if(!used[i])
            Dfs(i);
    int k = 0;
    ms(used, 0);
    for(int i = vs.size()-1; i >= 0; --i)
        if(!used[vs[i]])
            rDfs(vs[i], ++k);
    return k;
}
int main()
{
    int a, b;
    while(scanf("%d%d",&N,&M)!=EOF){
        ms(G, 0); ms(rG, 0); vs.clear(); ms(topo, 0);
        while(!q.empty()) q.pop();
        for(int i = 1; i <= N; ++i)
            scanf("%d",&f[i]);
        for(int i = 1; i <= M; ++i){
            scanf("%d%d",&a,&b);
            G[a].push_back(b);
            rG[b].push_back(a);
        }
        int n = scc();

        ms(used, 0);
        mark.clear(); //表示最初选用的点
        while(!q.empty()){
            P cur = q.top();
            q.pop();
            int u = cur.second;
            if(used[u]) continue;
            used[u] = true;
            mark.push_back(u);
            Dfs(u);
        }
        int ans = 0;
        for(int i = 0; i < mark.size(); ++i){
            int cur = inf;
            for(int j = 1; j <= N; ++j)
                if(topo[j]==topo[mark[i]])
                    cur = min(cur, f[j]);
            ans += cur;
        }
        printf("%d %d\n",mark.size(),ans);
    }

	return 0;
}

你可能感兴趣的:(图论)