hdu 2121+4009 最小树形图

http://www.cppblog.com/RyanWang/archive/2010/01/25/106427.html

简单来说,就是有向的最小生成树:

1、每个点找其最小的入边In[v] ? 如果有除跟节点以外的点找不到入边,则无解 : 否则答案累加In[v]

2、看看有没有环 ? 无环则已经找到解,返回答案 : 将环缩点

3、重新构图,每条边[u->v]的权值减去In[v],然后重复第一步

模板题:

hdu 2121:

View Code
  1 #include<iostream>
  2 #include<cstring>
  3 const int N=1010;
  4 const int inf=10000000;
  5 using namespace std;
  6 
  7 struct Edge{
  8     int u,v,w;
  9 }edge[N*N];
 10 
 11 int n,m,ansi;
 12 int In[N];
 13 int visited[N],ID[N];
 14 int pre[N];
 15 
 16 //root表示根结点,n是顶点树,m是边数
 17 //最小树形图邻接表版本,三步走,找最小入弧,找有向环,缩环为点
 18 int Directed_MST(int root,int n,int m){
 19     int u,v,i,cnt=0;
 20     while(true){
 21         //找最小入边
 22         for(i=0;i<n;i++)In[i]=inf;
 23         for(i=0;i<m;i++){
 24             u=edge[i].u;
 25             v=edge[i].v;
 26             if(edge[i].w<In[v]&&u!=v){
 27                 pre[v]=u;//u->v;
 28                 if(u==root)//记录是root从哪一条边到有效点的(这个点就是实际的起点)
 29                     ansi=i;
 30                 In[v]=edge[i].w;
 31             }
 32         }
 33         for(i=0;i<n;i++){
 34             if(i==root)continue;
 35             if(In[i]==inf)return -1;//说明存在点没有入边
 36         }
 37         //找环
 38         int cntcode=0;
 39         memset(visited,-1,sizeof(visited));
 40         memset(ID,-1,sizeof(ID));
 41         In[root]=0;
 42         //标记每一个环
 43         for(i=0;i<n;i++){
 44             cnt+=In[i];
 45             v=i;
 46             while(visited[v]!=i&&ID[v]==-1&&v!=root){
 47                 visited[v]=i;
 48                 v=pre[v];
 49             }
 50             //说明此时找到一个环
 51             if(v!=root&&ID[v]==-1){
 52                 //表示这是找到的第几个环,给找到的环的每个点标号
 53                 for(u=pre[v];u!=v;u=pre[u]){
 54                     ID[u]=cntcode;
 55                 }
 56                 ID[v]=cntcode++;
 57             }
 58         }
 59         if(cntcode==0)break;//说明不存在环
 60         for(i=0;i<n;i++){
 61             if(ID[i]==-1)
 62                 ID[i]=cntcode++;
 63         }
 64         //缩点,重新标记
 65         for(i=0;i<m;i++){
 66             int v=edge[i].v;
 67             edge[i].u=ID[edge[i].u];
 68             edge[i].v=ID[edge[i].v];
 69             //说明原先不在同一个环
 70             if(edge[i].u!=edge[i].v){
 71                 edge[i].w-=In[v];
 72             }
 73         }
 74         n=cntcode;
 75         root=ID[root];
 76     }
 77     return cnt;
 78 }
 79 
 80 
 81 int main(){
 82     while(scanf("%d%d",&n,&m)!=EOF){
 83         int sum=0;//添加的虚根点到每个点的权值比所有真实权值总和大1
 84         for(int i=0;i<m;i++){
 85             scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
 86             sum+=edge[i].w;
 87         }
 88         sum++;
 89         for(int i=m;i<m+n;i++){
 90             edge[i].u=n;//加的虚根点
 91             edge[i].v=i-m;
 92             edge[i].w=sum;
 93         }
 94         int ans=Directed_MST(n,n+1,n+m);
 95         if(ans==-1||ans>=2*sum){
 96             printf("impossible\n");
 97         }else 
 98             printf("%d %d\n",ans-sum,ansi-m);
 99         printf("\n");
100     }
101     return 0;
102 }

 hdu4009:

View Code
  1 #include<iostream>
  2 #include<cstring>
  3 const int N = 1010;
  4 const int inf=10000000;
  5 using namespace std;
  6 struct Point{
  7     int x,y,z;
  8 }p[N];
  9 
 10 struct Edge{
 11     int u,v,w;
 12 }edge[N*N];
 13 
 14 int X,Y,Z;
 15 int pre[N],ID[N],In[N],visited[N];
 16 
 17 //n表示点数,m表示边数,root表示根
 18  int Directed_MST(int root,int n,int m){
 19     int u,v,i,cnt=0;
 20     while(true)
 21     {
 22         for(i=0;i<n;i++)In[i]=inf;
 23         for(i=0;i<m;i++){
 24             u=edge[i].u;
 25             v=edge[i].v;
 26             if(edge[i].w<In[v]&&u!=v){
 27                 pre[v]=u;//找出每个点的最小入弧
 28                 In[v]=edge[i].w;
 29             }
 30         }
 31         //除根外有个节点无入弧,就返回false
 32         for(i=0;i<n;i++){
 33             if(i==root)continue;
 34             if(In[i]==inf)return -1;
 35         }
 36         In[root]=0;
 37         int cntcode=0;
 38         memset(ID,-1,sizeof(ID));
 39         memset(visited,-1,sizeof(visited));
 40         for(i=0;i<n;i++){
 41             cnt+=In[i];//进行缩圈
 42             v=i;
 43             while(visited[v]!=i&&ID[v]==-1&&v!=root){
 44                 visited[v]=i;
 45                 v=pre[v];
 46             }
 47             if(v!=root&&ID[v]==-1){
 48                 for(u=pre[v];u!=v;u=pre[u])
 49                     ID[u]=cntcode;
 50                 ID[v]=cntcode++;
 51             }
 52         }
 53         if(cntcode==0) break;
 54         for(i=0;i<n;i++){
 55             if(ID[i]==-1)
 56                 ID[i]=cntcode++;
 57         }
 58         for(i=0;i<m;i++){
 59             v=edge[i].v;//进行缩点,重新标记。
 60             edge[i].u=ID[edge[i].u];
 61             edge[i].v=ID[edge[i].v];
 62             if(edge[i].u!=edge[i].v)
 63                 edge[i].w-=In[v];
 64         }
 65         n=cntcode;
 66         root=ID[root];
 67     }
 68     return cnt;
 69 }
 70 
 71  int get_cost(Point& a,Point& b){
 72     int dis=abs(a.x-b.x)+abs(a.y-b.y)+abs(a.z-b.z);
 73     if(a.z>=b.z)
 74         return dis*Y;
 75     return dis*Y+Z;
 76 }
 77 
 78 int main()
 79 {
 80     int n,m,k,a;
 81     while(scanf("%d %d %d %d",&n,&X,&Y,&Z)==4 && (n||X||Y||Z)){
 82         m=0;
 83         for(int i=1;i<=n;i++)
 84             scanf("%d %d %d",&p[i].x,&p[i].y,&p[i].z);
 85         for(int i=1;i<=n;i++){
 86             scanf("%d",&k);
 87             while(k--){
 88                 scanf("%d",&a);
 89                 edge[m].u=i;
 90                 edge[m].v=a;
 91                 edge[m++].w=get_cost(p[i],p[a]);
 92             }
 93         }
 94         for(int i=1;i<=n;i++){
 95             edge[m].u=0;
 96             edge[m].v=i;
 97             edge[m++].w=p[i].z*X;
 98         }
 99         printf("%d\n",Directed_MST(0,n+1,m));
100     }
101     return 0;
102 }
原文地址:https://www.cnblogs.com/wally/p/2890979.html