Description:
一堆人需要联系,但如果x 可以联系 y,你联系了x就不用联系y了,你联系一个人都会有固定的花费,问你最小联系多少人,和最小花费
Solution:
Tarjan缩点,求出缩点的入度,如果为0则代表这个缩点需要联系一次,缩点的时候维护好根点到达该缩点的最小值即可
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int maxn = 1e4 + 1e2; const int maxm = 2e4 + 2e2; //tarjan int dfn[maxn],low[maxn],tot; //edge struct node{ int to,pre; }e[maxn]; int id[maxn],cnt; //color int col[maxn],cid; int in[maxn]; //stack int stk[maxn],siz; bool instk[maxn]; //Pdata int cost[maxn]; int mincost[maxn]; void init() { memset(id,-1,sizeof(id)); cnt = tot = cid = siz = 0; memset(dfn,0,sizeof(dfn)); memset(mincost,-1,sizeof(mincost)); memset(instk,0,sizeof(instk)); memset(stk,0,sizeof(stk)); memset(in,0,sizeof(in)); } void add(int from,int to) { e[cnt].to = to; e[cnt].pre = id[from]; id[from] = cnt++; } void tarjan(int now) { dfn[now] = low[now] = ++tot; stk[siz++] = now; instk[now] = true; for(int i = id[now];~i;i = e[i].pre) { int to = e[i].to; if(!dfn[to]) { tarjan(to); low[now] = min(low[now],low[to]); } else if(instk[to]) { low[now] = min(low[now],dfn[to]); } } if(dfn[now] == low[now]) { ++cid; while(siz > 0 && stk[siz] != now) { --siz; instk[stk[siz]] = false; col[stk[siz]] = cid; if(mincost[cid] == -1)mincost[cid] = cost[stk[siz]]; else mincost[cid] = min(mincost[cid],cost[stk[siz]]); } } } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { init(); for(int i = 1;i <= n;++i) scanf("%d",&cost[i]); int from,to; for(int i = 1;i <= m;++i) { scanf("%d%d",&from,&to); add(from,to); } for(int i = 1;i <= n;++i) { if(!dfn[i])tarjan(i); } for(int now = 1;now <= n;++now) { for(int i = id[now];~i;i = e[i].pre) { int to = e[i].to; if(col[now] != col[to]) { ++in[col[to]]; } } } int rescnt = 0,ressum = 0; for(int i = 1;i <= cid;++i) { if(!in[i]) { ressum += mincost[i]; ++rescnt; } } printf("%d %d ",rescnt,ressum); } return 0; }