题意:听说lcy帮大家预定了新马泰7日游,Wiskey真是高兴的夜不能寐啊,他想着得快点把这消息告诉大家,虽然他手上有所有人的联系方式,但是一个一个联系过去实在太耗时间和电话费了。他知道其他人也有一些别人的联系方式,这样他可以通知其他人,再让其他人帮忙通知一下别人。你能帮Wiskey计算出至少要通知多少人,至少得花多少电话费就能让所有人都被通知到吗?
想法:只要在提取一个连通块每一个点的(也就是染色的)地方,找出这个颜色块中的花费最小值保存即可。
#include<iostream> #include<cstring> #include<cstdio> #include<stack> #include<vector> #define me(x,y) memset(x,y,sizeof(x)) using namespace std; const int N=1000+5; vector<int>G[N]; stack<int>s; int low[N],dfn[N]; int instack[N],vis[N]; int paint[N]; int index,col; int indegree[N]; int money[N],min_money[N]; int Min(int a,int b) { if(a>b) return b; return a; } int Max(int a,int b) { if(a>b) return a; return b; } void Init(int n) { for(int i=1;i<=n;i++) { G[i].clear(); } index=col=1; me(low,0); me(dfn,0); me(instack,0); me(money,0); me(vis,0); me(indegree,0); //me(outdegree,0); me(paint,0); while(!s.empty()) s.pop(); } void Tarjan(int u) { dfn[u]=low[u]=index++; vis[u]=1; instack[u]=1; s.push(u); for(int i=0;i<G[u].size();i++) { int v=G[u][i]; if(!vis[v]) { Tarjan(v); low[u]=Min(low[u],low[v]); } else if(instack[v]) { low[u]=Min(low[u],dfn[v]); } } if(low[u]==dfn[u]) { min_money[col]=10000000; int k=s.top(); while(u!=k) { s.pop(); paint[k]=col; instack[k]=0; k=s.top(); //cout<<money[k]<<" "; if(money[k]<min_money[col]) { min_money[col]=money[k]; //cout<<"-----"<<min_money[col]<<endl; } } s.pop(); paint[u]=col; instack[u]=0; //cout<<money[u]<<" "<<endl; if(money[u]<min_money[col]) { min_money[col]=money[u]; } col++; } } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { Init(n); for(int i=1;i<=n;i++) { scanf("%d",&money[i]); } while(m--) { int a,b; scanf("%d%d",&a,&b); if(a==b) continue; G[a].push_back(b); } for(int i=1;i<=n;i++) { if(!vis[i]) { Tarjan(i); } } col--; for(int i=1;i<=n;i++) { for(int j=0;j<G[i].size();j++) { int a=i,b=G[i][j]; if(paint[a]!=paint[b]) { indegree[paint[b]]++; } } } int sum=0; int in=0; for(int i=1;i<=col;i++) { if(indegree[i]==0) { in++; sum+=min_money[i]; } } printf("%d %d\n",in,sum); } return 0; }