/*
思想略。
想说的是:妹纸啊,你这代码效率太低了吧,你说你写了多久?面壁思过去。。。
*/
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
const int NN=24;
const int MM=1000;
const int INF=0x1fffffff;
int n,m,v0,lim,ans,degree;
vector<int> adj;
struct Edge{ //存储原始边,Kruskal用
int u,v,w;
Edge(){}
Edge(int uu,int vv,int ww): u(uu),v(vv),w(ww){}
}edge[MM];
int ecnt;
struct Tree{ //生成树中的边,后期修改用
int u,v,next,w;
}te[MM];
int tcnt,head[NN];
void add(int u,int v,int w)
{
++tcnt;
te[tcnt].u=u;
te[tcnt].v=v;
te[tcnt].w=w;
te[tcnt].next=head[u];
head[u]=tcnt;
}
char h[NN][12];
int hash(char *s)
{
for (int i=1; i<=n; i++)
if (strcmp(s,h[i])==0) return i;
strcpy(h[++n],s);
return n;
}
void init()
{
ecnt=0; n=0;
scanf("%d",&m);
char s1[12],s2[12];
strcpy(s1,"Park"); v0=hash(s1); //度限制的点为v0
for (int i=1; i<=m; i++)
{
int w;
scanf("%s%s%d",s1,s2,&w);
int u=hash(s1);
int v=hash(s2);
edge[++ecnt]=Edge(u,v,w);
}
scanf("%d",&lim);
}
int p[NN];
int pp(int x) //Kruskal离不开并查集。。。
{
if (p[x]!=x) p[x]=pp(p[x]);
return p[x];
}
int cmp(const void *a,const void *b){
Edge *c=(Edge *)a;
Edge *d=(Edge *)b;
return c->w-d->w;
}
void Kruskal() //除去v0点的Kruskal(处理过后的生成树子块不一定是连通的),题目保证有解,点又不多,就没统计边了,怎么方便怎么写,死活不会超时。。。
{
tcnt=-1;
mem(head,-1);
qsort(edge+1,ecnt,sizeof(Edge),cmp);
for (int i=1; i<=ecnt; i++)
if (edge[i].u==v0 || edge[i].v==v0) adj.push_back(i);
for (int i=1; i<=n; i++) p[i]=i;
ans=0;
for (int i=1; i<=ecnt; i++)
{
if (edge[i].u==v0 || edge[i].v==v0) continue;
if (pp(edge[i].u)==pp(edge[i].v)) continue;
p[pp(edge[i].v)]=pp(edge[i].u);
ans+=edge[i].w;
add(edge[i].u,edge[i].v,edge[i].w);
add(edge[i].v,edge[i].u,edge[i].w);
}
}
void link_forest() //刚刚Kruskal时除去了v0点,这里把v0点和已生成的子块连起来,保证有解,就不用判断度degree与度限制lim的关系了,处理过后v0度不超lim的初步生成树。。。
{
degree=0;
for (int j=0; j<adj.size(); j++)
{
int i=adj[j];
if (pp(edge[i].u)==pp(edge[i].v)) continue;
p[pp(edge[i].v)]=pp(edge[i].u);
degree++;
ans+=edge[i].w;
add(edge[i].u,edge[i].v,edge[i].w);
add(edge[i].v,edge[i].u,edge[i].w);
edge[i].w=-1; //用过了,-1置之,小优化
}
}
int me[NN];
int vis[NN];
void get_Maxedge() //从v0点开始BFS一次计算当前生成树中各点到v0点的路径中权值最大的边me[i](此边不能与v0相接的边)
{
mem(vis,false);
vis[v0]=true;
te[0].w=0;
mem(me,0);
queue<int> q;
for (int i=head[v0]; i!=-1; i=te[i].next)
{
q.push(te[i].v);
vis[te[i].v]=true;
}
while (!q.empty())
{
int u=q.front();
q.pop();
for (int i=head[u]; i!=-1; i=te[i].next)
{
int v=te[i].v;
if (vis[v] || te[i].w==-1) continue;
vis[v]=true;
if (te[i].w>te[me[u]].w) me[v]=i;
else me[v]=me[u];
q.push(v);
}
}
}
void replace() //在v0的度不超lim的情况下试着以与v0相接的边替换已有边,得到更小的生成树
{
for (; degree<lim; degree++)
{
get_Maxedge();
int k,Max_rec=0;
for (int j=0; j<adj.size(); j++) //找能替换的最大差值,未用的v0的邻边(v0,u)与me[u]比较
{
int i=adj[j];
if (edge[i].w==-1) continue;
int u=edge[i].u==v0? edge[i].v:edge[i].u;
if (te[me[u]].w-edge[i].w>Max_rec)
Max_rec=te[me[u]].w-edge[k=i].w;
}
if (Max_rec==0) break; //找不到,更新完毕,退出
int u=edge[k].u==v0? edge[k].v:edge[k].u;
add(v0,u,edge[k].w);
add(u,v0,edge[k].w);
ans+=edge[k].w-te[me[u]].w;
te[me[u]].w=te[me[u]^1].w=-1; //边被更新,-1标记之
}
}
int main()
{
init();
Kruskal();
link_forest();
replace();
printf("Total miles driven: %d\n",ans);
return 0;
}
POJ1639-某点度限制的MST
最新推荐文章于 2021-05-11 17:15:21 发布