hdu 3639(强连通+缩点+建反向图)+hdu 3072(最小树形图)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3639
思路:先按一般的思路来,把杂乱的有向图通过缩点变成有向无环图,然后建反向图,并标记每个点的入度(最大值一定在反向图的入度为的点中)然后dfs一下下就可以了,最后就是在原图中找等于MAX的点就可以了。
View Code
1 #include<iostream> 2 #include<vector> 3 #include<stack> 4 const int MAXN=5000+10; 5 using namespace std; 6 vector<int>mp1[MAXN];//原图 7 vector<int>mp2[MAXN];//反向图 8 stack<int>S; 9 bool mark[MAXN];//标记元素是否在栈中 10 int color[MAXN];//缩点,染色 11 int to[MAXN];//入度 12 int n,m,cnt,_count; 13 int dfn[MAXN],low[MAXN]; 14 int num[MAXN];//用来保存所有的最大点 15 int dp[MAXN];//用于保存每个强连通分量的点数 16 17 //求强连通分量Tarjan算法 18 void Tarjan(int u){ 19 dfn[u]=low[u]=++cnt; 20 mark[u]=true; 21 S.push(u); 22 for(int i=0;i<mp1[u].size();i++){ 23 int v=mp1[u][i]; 24 if(dfn[v]==0){ 25 Tarjan(v); 26 low[u]=min(low[u],low[v]); 27 }else if(mark[v]){ 28 low[u]=min(low[u],dfn[v]); 29 } 30 } 31 if(low[u]==dfn[u]){ 32 int v; 33 do{ 34 v=S.top(); 35 S.pop(); 36 mark[v]=false; 37 color[v]=_count;//缩点,染色 38 dp[_count]++;//保存该强联通分量的点数 39 }while(u!=v); 40 _count++; 41 } 42 } 43 44 //计算反向图中每一个入度为0的点的最大值 45 int dfs(int u){ 46 mark[u]=true; 47 cnt+=dp[u]; 48 for(int i=0;i<mp2[u].size();i++){ 49 int v=mp2[u][i]; 50 if(!mark[v])dfs(v); 51 } 52 return cnt; 53 } 54 55 int main(){ 56 int _case,t=1; 57 scanf("%d",&_case); 58 while(_case--){ 59 scanf("%d%d",&n,&m); 60 for(int i=0;i<n;i++){ 61 mp1[i].clear(); 62 mp2[i].clear(); 63 } 64 for(int i=1;i<=m;i++){ 65 int x,y; 66 scanf("%d%d",&x,&y); 67 mp1[x].push_back(y); 68 } 69 memset(mark,false,sizeof(mark)); 70 memset(dfn,0,sizeof(dfn)); 71 memset(low,0,sizeof(low)); 72 memset(color,0,sizeof(color)); 73 memset(dp,0,sizeof(dp)); 74 memset(to,0,sizeof(to)); 75 memset(num,0,sizeof(num)); 76 _count=0,cnt=0; 77 for(int i=0;i<n;i++){ 78 if(dfn[i]==0){ 79 Tarjan(i); 80 } 81 } 82 //重新构图,建反向图 83 for(int i=0;i<n;i++){ 84 for(int j=0;j<mp1[i].size();j++){ 85 //不在同一个连通分量的点 86 if(color[i]!=color[mp1[i][j]]){ 87 mp2[color[mp1[i][j]]].push_back(color[i]); 88 to[color[i]]++;//入度 89 } 90 } 91 } 92 printf("Case %d: ",t++); 93 int MAX=-1,tag=0; 94 for(int i=0;i<_count;i++){ 95 //最大值一定在反向图中入度为0的点中 96 cnt=0; 97 if(to[i]==0){ 98 memset(mark,false,sizeof(mark)); 99 int cnt=dfs(i); 100 num[i]=cnt;//保存每一个入度0的最大值 101 MAX=max(MAX,cnt); 102 } 103 } 104 printf("%d\n",MAX-1); 105 //在原图中找最大的点 106 for(int i=0;i<n;i++){ 107 if(num[color[i]]==MAX){ 108 if(!tag){ 109 printf("%d",i); 110 tag=1; 111 }else{ 112 printf(" %d",i); 113 } 114 } 115 } 116 printf("\n"); 117 } 118 return 0; 119 }
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3072
思路:先缩点变成有向无环图,dp[i][j]表示缩点后的第i个连通分量到第j个连通分量的最小花费(环内花费为0),从而求每个点的最小入边就行了。
View Code
1 #include<iostream> 2 #include<vector> 3 #include<stack> 4 const int MAXN=100000+10; 5 const int N=400; 6 const int inf=1<<30; 7 using namespace std; 8 9 vector<int>mp[MAXN]; 10 stack<int>S; 11 int n,m,_count,cnt; 12 bool mark[MAXN]; 13 int dfn[MAXN],low[MAXN]; 14 int color[MAXN];//缩点,染色 15 int dp[N][N]; 16 struct Node{ 17 int x,y,cost; 18 }node[MAXN]; 19 20 //Tarjan算法求强连通分量 21 void Tarjan(int u){ 22 dfn[u]=low[u]=++cnt; 23 mark[u]=true; 24 S.push(u); 25 for(int i=0;i<mp[u].size();i++){ 26 int v=mp[u][i]; 27 if(dfn[v]==0){ 28 Tarjan(v); 29 low[u]=min(low[u],low[v]); 30 }else if(mark[v]){ 31 low[u]=min(low[u],dfn[v]); 32 } 33 } 34 if(low[u]==dfn[u]){ 35 int v; 36 do{ 37 v=S.top(); 38 S.pop(); 39 mark[v]=false; 40 color[v]=_count;//染色 41 }while(u!=v); 42 _count++; 43 } 44 } 45 46 47 int main(){ 48 while(~scanf("%d%d",&n,&m)){ 49 for(int i=0;i<n;i++)mp[i].clear(); 50 for(int i=0;i<m;i++){ 51 scanf("%d%d%d",&node[i].x,&node[i].y,&node[i].cost); 52 mp[node[i].x].push_back(node[i].y); 53 } 54 memset(mark,false,sizeof(mark)); 55 memset(dfn,0,sizeof(dfn)); 56 memset(low,0,sizeof(low)); 57 memset(color,0,sizeof(color)); 58 _count=0,cnt=0; 59 for(int i=0;i<n;i++){ 60 if(dfn[i]==0){ 61 Tarjan(i); 62 } 63 } 64 for(int i=0;i<_count;i++){ 65 for(int j=0;j<_count;j++){ 66 dp[i][j]=inf; 67 } 68 } 69 for(int i=0;i<m;i++){ 70 if(color[node[i].x]!=color[node[i].y]){ 71 dp[color[node[i].x]][color[node[i].y]]=min( 72 dp[color[node[i].x]][color[node[i].y]], 73 node[i].cost); 74 } 75 } 76 memset(mark,false,sizeof(mark)); 77 int ans=0; 78 //选择每个点的最小入边 79 for(int i=0;i<_count;i++){ 80 int tmp=inf,l=0; 81 for(int j=0;j<_count;j++){ 82 for(int k=0;k<_count;k++){ 83 if(!mark[k]&&tmp>dp[j][k]){ 84 tmp=dp[j][k]; 85 l=k; 86 } 87 } 88 } 89 if(tmp==inf)break; 90 ans+=tmp; 91 mark[l]=true; 92 } 93 printf("%d\n",ans); 94 } 95 return 0; 96 }