Summer Holiday
Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3909 Accepted Submission(s): 1764
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
思路:如果这是一个DAG图,威士忌只需联系所有入度为0的点,但是这里有环,我们考虑把它变成无环的DAG,因为有些点集可以看成一个整体,联系其中一个其余都能收到消息,这就是强连通子图,把他们缩成一个点,缩点同时记录“点”内哪个点与威士忌的联系花费最少,缩完后再找入度为0的“点”即可。
# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
const int maxn = 1e3+30;
struct node
{
int u, v, next;
}edge[maxn<<2];
int Next[maxn], belong[maxn], in[maxn], C[maxn], c[maxn], h, tot, cnt, ans;
int dep[maxn], low[maxn], stk[maxn], is_stk[maxn];
void add_edge(int u, int v)
{
edge[tot] = node{u,v,Next[u]};
Next[u] = tot++;
}
void init()
{
tot = cnt = ans = h = 0;
memset(C, 0x3f, sizeof(C));
memset(dep, 0, sizeof(dep));
memset(in, 0, sizeof(in));
memset(is_stk, 0, sizeof(is_stk));
memset(low, 0, sizeof(low));
memset(Next, -1, sizeof(Next));
memset(belong, 0, sizeof(belong));
}
void dfs(int u)
{
dep[u] = low[u] = ++h;
stk[cnt++] = u;
is_stk[u] = 1;
for(int i=Next[u]; i!=-1; i=edge[i].next)
{
int v = edge[i].v;
if(!dep[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(is_stk[v] && dep[v] < low[u])
low[u] = dep[v];
}
if(dep[u] == low[u])
{
int j;
++ans;
do
{
j = stk[--cnt];
is_stk[j] = 0;
belong[j] = ans;
C[ans] = min(C[ans], c[j]);
}while(j != u);
}
}
int main()
{
int n, m;
while(~scanf("%d%d",&n,&m))
{
init();
for(int i=1; i<=n; ++i) scanf("%d",&c[i]);
for(int i=0; i<m; ++i)
{
int a, b;
scanf("%d%d",&a,&b);
add_edge(a, b);
}
for(int i=1; i<=n; ++i)
if(!dep[i]) dfs(i);
for(int i=1; i<=n; ++i)
{
for(int j=Next[i]; j!=-1; j=edge[j].next)
{
int u=edge[j].u, v = edge[j].v;
if(belong[u] != belong[v])
++in[belong[v]];
}
}
int sum = 0, man = 0;
for(int i=1; i<=ans; ++i)
{
if(in[i] == 0)
{
++man;
sum += C[i];
}
}
printf("%d %d\n",man, sum);
}
return 0;
}
本文解析了一道关于图论的竞赛题目,通过寻找强连通子图并进行缩点处理,最终确定最少的通知人数和最小的电话费用。适用于对图论和算法竞赛感兴趣的学习者。
6014

被折叠的 条评论
为什么被折叠?



