http://poj.org/problem?id=1639
最小限制生成树 黑书上有 推荐 我看了上面的解析自己闷头写了一个 不好 我自己看着都乱 唉就这样吧
大体就是 先求出除了关键点以外的点形成的若干最小生成树 然后把这些最小生成树和关键点用最短的距离联系起来
然后不断点加与关键点之间的的连线 每联一个就会多一个环 然后就得删一个边 把环取消 不过加的边 和删的边的选取必须是加边减去删边的
值最小 一旦限度达到 或者更新后不如原来的小了 则放弃更新
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<queue>
#include<map>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int N=101;
const int M=10000;
struct node
{
int k;
struct tt *next;
}mem[N];//邻接表头 k 表示到此点到基本点的边中 不和基本点相连的边中最长的边
struct tt
{
int wside;
struct tt *next;
int j;
};//wside 代表哪条边
struct lin
{
int x,y;
int dist;
}linkside[M];//记录边的两端 和距离
int have[M];//此条边的状态 1 表示最图中 -1 表示删掉不再用 0表示待定
int f[N];//最小生成树中的最终父节点 和dfs是标记是否搜过那个点
int m,k,limit,n;//m为边的个数 n为点的个数 limit为限度 k和基本点相连的度
map<string,int>str;
int MIN,V0;//在限制的情况下最小树 和基本点
int findx(int x)//你懂得
{
if(f[x]!=x)
f[x]=findx(f[x]);
return f[x];
}
bool cmp(lin a,lin b)
{
return a.dist<b.dist;
}
void build(int ,int ,int );
void prim()
{
sort(linkside,linkside+m,cmp);
memset(have,0,sizeof(have));
for(int i=0;i<n;++i)
f[i]=i;
for(int i=0;i<m;++i)
{
int l1=linkside[i].x,l2=linkside[i].y;
if(findx(l1)==findx(l2))//如果有环 此边不要
{
have[i]=-1;
continue;
}
if(l1!=V0&&l2!=V0)//不是有基本点的边
{
have[i]=1;//记录边状态
MIN+=linkside[i].dist;//增加全局变量
f[findx(l1)]=l2;
build(l1,l2,i);//建树
build(l2,l1,i);
}else
{
if(l1==V0)//如果为基本点相连的边 建树但不记录
build(l1,l2,i);
else
build(l2,l1,i);
}
}
}
void build(int i,int j,int l)
{
struct tt *t=new tt;
t->j=j;
t->wside=l;
t->next=mem[i].next;
mem[i].next=t;
}
void Dele(int n)
{
struct tt *t;
for(int i=0;i<=n;++i)
{
while(mem[i].next!=NULL)
{
t=mem[i].next;
mem[i].next=t->next;
delete (t);
}
}
}
void dfs(int x,int k)
{
f[x]=1;
mem[x].k=k;//更新到此点为止最长边(不含和基本点相连的)
struct tt *t=mem[x].next;
while(t!=NULL)
{
if(!f[t->j]&&have[t->wside]==1)
{
int down=(linkside[k].dist<linkside[t->wside].dist)?t->wside:k;//选择较长的向下更新
dfs(t->j,down);
}
t=t->next;
}
}
void update()
{
memset(f,0,sizeof(f));
f[V0]=1;
struct tt *t=mem[V0].next;
while(t!=NULL)
{
if(have[t->wside]==1)
{
dfs(t->j,m);//第m条边 已被定义为距离最小
}
t=t->next;
}
}
int main()
{
//freopen("data.txt","r",stdin);
string sstemp1,sstemp2,park="Park";
while(scanf("%d",&m)!=EOF)
{
str.clear();
int I=0;
V0=-1;
for(int i=0;i<m;++i)
{
int l1,l2,d;
cin>>sstemp1>>sstemp2>>d;
if(str.find(sstemp1)==str.end())//用map 把字符串对应成数字
{str[sstemp1]=I;l1=I++;}
else
l1=str[sstemp1];
if(str.find(sstemp2)==str.end())
{str[sstemp2]=I;l2=I++;}
else
l2=str[sstemp2];
linkside[i].x=l1;
linkside[i].y=l2;
linkside[i].dist=d;
if(V0==-1)//记录基本点
{
if(sstemp1==park)
V0=l1;
if(sstemp2==park)
V0=l2;
}
}
n=I;
scanf("%d",&limit);
MIN=0;
prim();//建立除了基本点以外的若干 最小生成树
linkside[m].dist=-1;
k=0;
for(int i=0;i<m;++i)
{
if(have[i]!=0)
continue;
int l1=linkside[i].x,l2=linkside[i].y;
if(findx(l1)==findx(l2))
continue;
if(l1==V0)//对和基本点相连的边进行选最小的更新 但不能形成环 做相应的记录
{
have[i]=1;
f[findx(l1)]=l2;
++k;
MIN+=linkside[i].dist;
}
if(l2==V0)
{
have[i]=1;
f[findx(l1)]=l2;
++k;
MIN+=linkside[i].dist;
}
}
update();//更新每个点到基本点的边中最长的(不含和基本点直接相连的)
for(;k<limit;++k)
{
int wtemp=-1,ktemp,mitemp;
struct tt *t=mem[V0].next;
while(t!=NULL)
{
if(have[t->wside]==0)//选新的和基本点相连的点
{
if(wtemp==-1||linkside[t->wside].dist-linkside[mem[t->j].k].dist<mitemp)
{
wtemp=t->wside;
mitemp=linkside[t->wside].dist-linkside[mem[t->j].k].dist;
ktemp=mem[t->j].k;
}
}
t=t->next;
}
if(wtemp==-1||mitemp>=0)//如果没选到 或者更新后无法跟新最终答案 则没有必要继续更新
{
break;
}
have[wtemp]=1;
have[ktemp]=-1;
MIN+=mitemp;
update();
}
printf("Total miles driven: %d\n",MIN);
Dele(n);
}
return 0;
}
浙公网安备 33010602011771号