HDU最可愛的地方就是他的原創題目的中文描述,哎,還是母語看著親切啊,老毛子的文字很糾結啊……
還是強連通縮點建圖,tarjan實現,算是經典應用了也
建完圖,找所有入度為0的點,就是最少要得到資訊的點,然後這個點的權值是這個強連通分量裡面所有點權值中的最小值,把他們加起來就好了。
代碼:
#include<cstdio>#include<cstring>#include<stack>#include<climits>using namespace std;const int N = 1001;struct Edge{int s,e,next;}edge1[2*N],edge2[2*N];int n,m,e_num1,e_num2,vis_num,cnt;int head[N],tim[N],low[N],instack[N],belong[N],price[N],de[N];void AddEdge(int a,int b,Edge edge[],int &e_num){edge[e_num].s=a; edge[e_num].e=b; edge[e_num].next=head[a]; head[a]=e_num++;}void getmap(){int i,a,b;for(i=1;i<=n;i++)scanf("%d",&price[i]);e_num1=0;memset(head,-1,sizeof(head));while(m--){scanf("%d%d",&a,&b); AddEdge(a,b,edge1,e_num1);}}stack <int> st;void tarjan(int x){//注意這是個遞迴過程int i,j;tim[x]=low[x]=++vis_num;//tim是時間戳記,標記該點出現的時間,low是該點能到達的點中時間戳記值的最小值st.push(x);instack[x]=1;for(i=head[x];i!=-1;i=edge1[i].next){int u=edge1[i].e;if(tim[u]==-1){//時間戳記為-1,也就是沒有訪問過tarjan(u);if(low[x]>low[u])low[x]=low[u];//更新low值,取能到達的最上(小)位置}//點u已經訪問過且在棧中,時間戳記比點i小,那麼low值當然比點i小//(因為tim,low都初始化為vis_num),那麼low[i]可以更新else if(instack[u] && tim[u]<low[x])low[x]=tim[u];}if(tim[x]==low[x]){cnt++;do{//退棧,直到看到根為止j=st.top();st.pop();instack[j]=0;belong[j]=cnt;//標記該點所屬分量的標號}while(j!=x);}}int fun(int x){int i,min=INT_MAX;for(i=1;i<=n;i++){if(belong[i]==x && price[i]<min)min=price[i];}return min;}void solve(){int i;cnt=vis_num=0;memset(tim,-1,sizeof(tim));memset(low,0,sizeof(low));memset(instack,0,sizeof(instack));for(i=1;i<=n;i++){if(tim[i]==-1)tarjan(i);}e_num2=0;memset(head,-1,sizeof(head));memset(de,0,sizeof(de));for(i=0;i<e_num1;i++){int j=edge1[i].s;int k=edge1[i].e;if(belong[j] != belong[k]){AddEdge(belong[j],belong[k],edge2,e_num2);de[belong[k]]++;}}int sum=0,count=0;for(i=1;i<=cnt;i++){if(de[i]==0){sum+=fun(i);count++;}}printf("%d %d\n",count,sum);}int main(){while(~scanf("%d%d",&n,&m)){getmap();solve();}return 0;}