题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=1827
题意:
w i s k e y wiskey wiskey需要联系 [ 1... n ] [1...n] [1...n]这 n n n个人,并且 w i s k e y wiskey wiskey与这 n n n个人之间的联系需要费用,与每个人联系的费用已知这 n n n个人如果有 ( a , b ) (a,b) (a,b),即 a a a认识 b b b,那么 a a a可以联系 b b b且不需要费用,联系是单向的,且已给定一些 ( a , b ) (a,b) (a,b),问 w i s k e y wiskey wiskey需要联系到所有人,他需要联系的最少人数和最小费用
分析:
找出所有的连通量,将连通量看成点,那么只要找入度为零的连通量即可(画图模拟),然后在每个入度为0的连通量内,分别找需要费用最小的点,那么 w i s k e y wiskey wiskey只需要和这些人联系,即可;
代码:
#include<iostream>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int inf=0x7f7f7f7f;
const int maxn=2e4+50;
const int N=2e4+50;
typedef long long ll;
typedef struct{
int u,v,next;;
}Edge;
Edge e[2*maxn];
int cnt,head[maxn];
void add(int u,int v){
e[cnt].u=u;
e[cnt].v=v;
/*e[cnt].f=f;*/
e[cnt].next=head[u];
head[u]=cnt++;
/* e[cnt].u=v;
e[cnt].v=u;
e[cnt].w=0;
e[cnt].f=-f;
e[cnt].next=head[v];
head[v]=cnt++;*/
}
int read()
{
int x = 0;
int f = 1;
char c = getchar();
while (c<'0' || c>'9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0'&&c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return x*f;
}
//a是费用,b[u]是说明u是属于哪一个连通量的,stac是栈,存连通量节点
//vis[u]为1说明u点在栈里,为0则不在,in存连通量入度,minn[u]则是第u个连通量内的费用最小值
int t,res,n,m,cost,num,a[maxn],b[maxn],dfn[maxn],low[maxn],stac[maxn],top,_time,vis[maxn],in[maxn],minn[maxn];
void tarjan(int u){
stac[top++]=u;
dfn[u]=low[u]=++_time;
vis[u]=1;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].v;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v])low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
res++;
do{
t=stac[--top];
vis[t]=0;
b[t]=res;//标记t是属于第res个连通量的
minn[res]=min(minn[res],a[t]);//连通量内费用最小值
}while(t!=u);
}
}
int main() {
while(scanf("%d%d",&n,&m)!=EOF){
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(vis,0,sizeof(vis));
memset(head,-1,sizeof(head));
memset(stac,0,sizeof(stac));
memset(minn,inf,sizeof(minn));
memset(in,0,sizeof(in));
num=top=_time=cost=res=cnt=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int a,c;
for(int i=0;i<m;i++){
scanf("%d %d",&a,&c);
add(a,c);
}
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
for(int i=0;i<cnt;i++){
int u=e[i].u,v=e[i].v;
if(b[u]!=b[v]) //u,v属于不同联通量,且u指向v则
in[b[v]]++; //v所在连通量入度加1
}
for(int i=1;i<=res;i++){
if(!in[i])cost+=minn[i],num++;
}
cout<<num<<" "<<cost<<endl;
}
}
(仅供个人理解)