HDU 1827 Summer Holiday

来源:互联网 发布:win 2008 如何打开端口 编辑:程序博客网 时间:2024/04/25 17:30

题意:

你需要打电话通知n个人,通知第i个人会花a[i]的钱,但是可以叫他去通知其他能通知的人,先给你m个关系表示a能通知b,但b不一定能通知a。问你最少需要通知多少个人和最少需要花费多少钱才能使全部人都知道。

思路:

一开始以为是简单地找入度为0的点,但是样例就过不了了(这个样例特别良心)。因为存在1->2,2->1的强连通分量,如果没人能通知他们,那么他们就不会被通知到。所以我们就需要用Tarjan来把所有的强连通分量缩成一个点,然后求出有几个入度为0的点,并在每个入度为0的点的强连通分量中找到一个话费最小的人来通知。

#include<cstdio>#include<vector>#include<stack>#include<cstring>#include<queue>#include<map>#include<algorithm>using namespace std;const int MAXN=1005;int n,m,time,cnt,a[MAXN],dfn[MAXN],low[MAXN],id[MAXN],in[MAXN],cost[MAXN];bool vis[MAXN];vector<int> gr[MAXN],se[MAXN];stack<int> s;void init(){for(int i=1;i<=n;i++){gr[i].clear();}while(!s.empty()){s.pop();}memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));memset(in,0,sizeof(in));memset(vis,false,sizeof(vis));time=0;cnt=0;}void Tarjan(int u){int v;vis[u]=true;dfn[u]=low[u]=++time;s.push(u);for(int i=0;i<gr[u].size();i++){v=gr[u][i];if(!dfn[v]){Tarjan(v);low[u]=min(low[u],low[v]);}else if(vis[v]){low[u]=min(low[u],dfn[v]);}}if(low[u]==dfn[u]){cnt++;while(!s.empty()){v=s.top();s.pop();vis[v]=false;se[cnt].push_back(v);id[v]=cnt;if(u==v) break;}}}int main(){while(~scanf("%d%d",&n,&m)){for(int i=1;i<=n;i++){scanf("%d",&a[i]);}init();int u,v;for(int i=1;i<=m;i++){scanf("%d%d",&u,&v);gr[u].push_back(v);}for(int i=1;i<=n;i++){if(!dfn[i]){Tarjan(i);}}memset(cost,63,sizeof(cost));for(int i=1;i<=n;i++){cost[id[i]]=min(cost[id[i]],a[i]);for(int j=0;j<gr[i].size();j++){int v=gr[i][j];if(id[i]!=id[v]){in[id[v]]++;}}}int ans1=0,ans2=0;for(int i=1;i<=cnt;i++){if(!in[i]){ans1++;ans2+=cost[i];}}printf("%d %d\n",ans1,ans2);}return 0;}


0 0
原创粉丝点击