Summer Holiday
Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4574 Accepted Submission(s): 2078
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计算出至少要通知多少人,至少得花多少电话费就能让所有人都被通知到吗?
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。
第一行两个整数N和M(1<=N<=1000, 1<=M<=2000),表示人数和联系对数。
接下一行有N个整数,表示Wiskey联系第i个人的电话费用。
接着有M行,每行有两个整数X,Y,表示X能联系到Y,但是不表示Y也能联系X。
Output
输出最小联系人数和最小花费。
每个CASE输出答案一行。
每个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
分析:
1.属于同一个强连通分量的点之间可以互相到达,所以可以缩成一个点,
2.缩点之后就构成了一个新图,统计新图入度为0的点,该点就是需要通知的人数
3.花费就是该点的强连通分量里面选择一个价值最小的点(同一个颜色的点中选择价值最小的点)
染色缩点!!!,学习了
code:
#include<stdio.h> #include<iostream> #include<math.h> #include<string.h> #include<set> #include<map> #include<list> #include<math.h> #include<queue> #include<algorithm> using namespace std; typedef long long LL; #define INF 0x7fffffff int mon1[13]= {0,31,28,31,30,31,30,31,31,30,31,30,31}; int mon2[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31}; int dir[4][2]= {{0,1},{0,-1},{1,0},{-1,0}}; int getval() { int ret(0); char c; while((c=getchar())==' '||c==' '||c==' '); ret=c-'0'; while((c=getchar())!=' '&&c!=' '&&c!=' ') ret=ret*10+c-'0'; return ret; } #define max_v 1005 #define mem(a,x) memset(a,x,sizeof(a)) vector<int> G[max_v]; int dfn[max_v]; int low[max_v]; int vis[max_v]; int indgree[max_v]; int color[max_v]; int stk[max_v]; int v[max_v]; int sig,t,cnt; int n,m; void init() { for(int i=1;i<=n;i++) G[i].clear(); mem(dfn,0); mem(low,0); mem(vis,0); mem(indgree,0); mem(stk,0); mem(v,0); mem(color,0); sig=0;//强连通分量数 t=-1;//栈顶指针 cnt=1;//深度标识 } void tarjan(int u) { vis[u]=1; dfn[u]=low[u]=cnt++; stk[++t]=u;//入栈 for(int i=0;i<G[u].size();i++) { int v=G[u][i]; if(vis[v]==0) tarjan(v); if(vis[v]==1) low[u]=min(low[u],low[v]); } if(dfn[u]==low[u]) { sig++; do { vis[stk[t]]=-1;//染色标记 color[stk[t]]=sig;//将该强连通分量的点染成一个颜色 }while(stk[t--]!=u);//栈结构储存需要染色的点 } } int f(int x)//统计属于x颜色的点中价值最小的点的价值 { int minv=INF; for(int i=1;i<=n;i++) { if(color[i]==x) { minv=min(minv,v[i]); } } return minv; } int main() { int x,y; while(~scanf("%d %d",&n,&m)) { init(); for(int i=1;i<=n;i++) scanf("%d",&v[i]); for(int i=1;i<=m;i++) { scanf("%d %d",&x,&y); if(count(G[x].begin(),G[x].end(),y)==0)//重边 G[x].push_back(y); } //tarjan求强连通分量 for(int i=1;i<=n;i++) { if(vis[i]==0) tarjan(i); } //统计缩点之后新图的点的入度 for(int i=1;i<=n;i++) { for(int j=0;j<G[i].size();j++) { int v=G[i][j]; if(color[v]!=color[i]) indgree[color[v]]++; } } int ans=0,cost=0; for(int i=1;i<=sig;i++) { if(indgree[i]>0)//只有入读为0的点需要打电话 continue; ans++; cost+=f(i); } printf("%d %d ",ans,cost); } return 0; }