Summer Holiday
Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 976 Accepted Submission(s): 415
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
思路:强连通缩点后求入度为零的点,在从入度为零的点(强连通分量)中选择花费最小的点,花费求和即可。
wa的原因:tarjian算法中else忘记判断是否在栈中。
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<vector>
using namespace std;
#define max_n 1005
#define max_e 250002
#define inf 99999999
int stack[max_n],top;//栈
int isInStack[max_n];//是否在栈内
int low[max_n],dfn[max_n],tim;//点的low,dfn值;time从1开始
int node_id;
int head[max_n],s_edge;//邻接表头 s_edge从1开始
int gro_id[max_n];
int n,m;
int val[max_n];
int in[max_n];
struct Node
{
int to;
int next;
} edge[max_e];
void init()
{
top=0;
node_id=0;
memset(isInStack,0,sizeof(isInStack));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
tim=0;
memset(in,0,sizeof(in));
memset(head,0,sizeof(head));
s_edge=0;
memset(edge,0,sizeof(edge));
}
void addedge(int u,int v)
{
s_edge++;
edge[s_edge].to=v;
edge[s_edge].next=head[u];
head[u]=s_edge;
}
int min(int a,int b)
{
if(a<b)return a;
else return b;
}
void tarjan(int u)
{
//low值为u或u的子树能够追溯到得最早的栈中节点的次序号
stack[top++]=u;
isInStack[u]=1;
dfn[u]=++tim; //记录点u出现的记录,并放在栈中
low[u]=tim;
int e,v;
for(e=head[u]; e; e=edge[e].next) //如果是叶子节点,head[u]=0,edge[e].next=0;
{
v=edge[e].to;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(isInStack[v])
low[u]=min(low[u],dfn[v]);
}
int j;
if(dfn[u]==low[u])
{
node_id++;
while(j=stack[--top])
{
isInStack[j]=0;
gro_id[j]=node_id;
if(j==u)break;
}
}
}
void find()
{
for(int i = 1 ; i <=n ; ++i)
{
if(!dfn[i])
{
tarjan(i);
}
}
}
vector<int> vec[max_n];
int main()
{
int a,b;
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
for(int i=1; i<=n; i++)
{
scanf("%d",&val[i]);
vec[i].clear();
}
for(int i = 0 ; i <m ; ++i)
{
scanf("%d%d",&a,&b);
vec[a].push_back(b);
addedge(a,b);
}
find();
for(int i=1; i<=n; i++)
{
for(int j=0; j<vec[i].size(); j++)//求入度为零的点
{
if(gro_id[i]!=gro_id[vec[i][j]])
in[gro_id[vec[i][j]]]++;
}
}
int sum=0,sum1=0;
for(int i=1; i<=node_id; i++)
{
if(in[i]==0)
{
int mm=inf;
for(int j=1; j<=n; j++)
{
if(gro_id[j]==i)
{
if(mm>val[j])
mm=val[j];
}
}
sum++;
sum1+=mm;
}
}
printf("%d %d\n",sum,sum1);
}
return 0;
}