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 }

 

 

你可能感兴趣的:(HDU)