[ACM]最小度限制生成树

最小度限制生成树是指某一个节点的度限制为k的最小生成树。具体的做法去看论文或者黑书吧,这里不详细讲了。

理解最小度限制生成树的解法,最主要的就是看懂如何由m度生成树求m+1度生成树,另外要理解无解的情况。

今天终于把模板整理出来了,prim那里用了优先队列优化,添加删除边破环那里用的是动态规划。试了试PKU1639这道题,结果正确,但是由于数据水,不知道效率如何。

贴出模板:

  1 #define maxn 505
  2 struct str
  3 {
  4     int v,c,n;
  5 }edge[maxn<<1];//邻接表
  6 int head[maxn],cnt;
  7 void init()
  8 {
  9     memset(head,-1,sizeof(head));
 10     cnt=0;
 11 }
 12 void addEdge(int u,int v,int c)
 13 {
 14     edge[cnt].v=v;
 15     edge[cnt].c=c;
 16     edge[cnt].n=head[u];
 17     head[u]=cnt++;
 18     edge[cnt].v=u;
 19     edge[cnt].c=c;
 20     edge[cnt].n=head[v];
 21     head[v]=cnt++;
 22 }
 23 int dis[maxn],best[maxn],index[maxn];
 24 //dis是prim里到某节点的最短距离,best具体含义看论文,index是best对应的边号
 25 int incom[maxn],com;//记录节点在哪个联通分量里
 26 bool vis[maxn],intree[maxn<<1];
 27 struct node
 28 {
 29     int u,c,e;//u是邻接表里对应的那个v,c是边权,e是边号
 30     node(){}
 31     node(int _u,int _c,int _e):u(_u),c(_c),e(_e){}
 32     bool operator <(const node a)const{return a.c<c;}
 33 };//优先队列数据结构
 34 priority_queue<node>q;
 35 int prim(int n,int s)
 36 {
 37     for(int i=0;i<=n;i++)dis[i]=inf;
 38     memset(vis,0,sizeof(vis));vis[s]=true;
 39     memset(intree,0,sizeof(intree));//判断边是否在生成树中
 40     int val=0;com=0;
 41     for(int i=1;i<=n;i++)if(!vis[i])//求各个连通分量的最小生成树
 42     {
 43         dis[i]=0;++com;q.push(node(i,0,-1));
 44         while(!q.empty())
 45         {
 46             int u=q.top().u,e=q.top().e,c=q.top().c;q.pop();
 47             if(vis[u])continue;
 48             val+=c;incom[u]=com;
 49             vis[u]=intree[e]=intree[e^1]=true;
 50             for(int j=head[u];j!=-1;j=edge[j].n)
 51             {
 52                 int v=edge[j].v,c=edge[j].c;
 53                 if(vis[v])continue;
 54                 if(dis[v]>c)dis[v]=c,q.push(node(v,c,j));
 55             }
 56         }
 57     }
 58     memset(vis,0,sizeof(vis));//vis数组复用,判断某节点所在的连通分量是否已经连接
 59     for(int i=head[s];i!=-1;i=edge[i].n)
 60     {
 61         int v=edge[i].v,c=edge[i].c;
 62         q.push(node(v,c,i));
 63     }
 64     while(!q.empty())//把各个分量连起来,这样就求出m度最小生成树
 65     {
 66         int v=q.top().u,c=q.top().c,e=q.top().e;
 67         q.pop();
 68         if(!vis[incom[v]])
 69             val+=c,intree[e]=intree[e^1]=vis[incom[v]]=true;
 70     }
 71     return val;
 72 }
 73 void dfs(int u,int fa,int n,int s)//更新best
 74 {
 75     for(int i=head[u];i!=-1;i=edge[i].n)if(intree[i])
 76     {
 77         int v=edge[i].v,c=edge[i].c;
 78         if(v==fa)continue;
 79         if(u==s)best[v]=-inf,index[v]=i;
 80         else
 81         {
 82             if(best[u]>c)best[v]=best[u],index[v]=index[u];
 83             else best[v]=c,index[v]=i;
 84         }
 85         dfs(v,u,n,s);
 86     }
 87 }
 88 //int mval;
 89 int klimit(int n,int s,int k)//求k度最小生成树,n是节点个数,s是度被限制的节点,k为限制的度数
 90 {
 91     int val=prim(n,s);
 92     //mval=val;
 93     if(k<com)return inf;//如果k小于连通分量个数,则无解
 94     best[s]=-inf;dfs(s,-1,n,s);
 95     for(int i=com+1;i<=k;i++)
 96     {
 97         int mn=inf,ed=-1;
 98         for(int j=head[s];j!=-1;j=edge[j].n)if(!intree[j])
 99         {
100             int v=edge[j].v,c=edge[j].c;
101             if(mn>c-best[v])mn=c-best[v],ed=j;
102         }
103         if(ed==-1)return inf;//无法再使度扩大则也是无解的
104         int v=edge[ed].v,dx=index[v];
105         val+=mn;
106         //mval=min(val,mval);
107         intree[ed]=intree[ed^1]=true;
108         intree[dx]=intree[dx^1]=false;
109         best[v]=-inf,dfs(v,s,n,s);
110     }
111     return val;
112 }

如果是求某节点正好为k度则就是上面的模板,如果节点的度不大于k则将上面mval的三条注释取消掉,最后结果就是mval。

原文地址:https://www.cnblogs.com/mcflurry/p/2964890.html