POJ 1639 Picnic Planning

  http://poj.org/problem?id=1639

  题目大意:给定一个图,求最小生成树,其中某个节点的度数小于K。

  这就是一个最小k限度生成树,我写了好久好久,也有我不熟悉STL的原因,CE了好久……

  求最小K度生成树的方法:首先去掉特殊点(v0),然后图会成为x个联通块,求x次最小生成树,然后将这x个生成树连接到v0上。

  每次寻找一个点p,满足点p不与v0相联系(边不在生成树上,不是不相连),且与点p相关的在生成树上的边大于点p到v0的距离,舍弃原来的边,并将点p与v0相连。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <utility>
#include <iterator>
#include <cassert>
#include <cctype>
#include <list>
#include <bitset>
#include <cmath>
#include <functional>
#include <set>
#include <sstream>
#include <ctime>
#define mn 301
#define mm 1000
#define inf ~0u>>1
using namespace std;

struct EDGE{
	int pnt,dist;
	EDGE *pre;
	EDGE (){}
	EDGE(int _pnt,int _dist,EDGE *_pre):pnt(_pnt),dist(_dist),pre(_pre){}
}Edge[mm*2],*SP=Edge,*edge[mm];
map<string,int> NameList;
char place1[100],place2[100];
int x,y,z,n,names=1,MST,belong[mn],size[mn],blocks,dist[mn],pre[mn],big[mn],K,Link[mn],Point[mn],Degree,places;
bool use[mn][mn],vis[mn];

inline void addedge(int a,int b,int c){
	edge[a]=new(++SP)EDGE(b,c,edge[a]);
	edge[b]=new(++SP)EDGE(a,c,edge[b]);
}

int NameOrder(char s[100]){
	if(NameList.find(s)==NameList.end()) NameList[s]=++names;
	else return NameList[s];
	return names;
}

void Count_Connection_Blocks(int x){
	vis[x]=true,belong[x]=blocks,size[blocks]++;
	for(EDGE *j=edge[x];j;j=j->pre)
		if(!vis[j->pnt]) Count_Connection_Blocks(j->pnt);
}

void Prim(int cur){	
	for(int i=1;i<=names;i++) dist[i]=inf;
	for(int i=1;i<=names;i++)
		if(belong[i]==cur){
			dist[i]=0;
			break;
		}
	for(int k=1;k<=size[cur];k++){
		int mind=inf,minp;
		for(int i=1;i<=names;i++)
			if(!vis[i] && mind>dist[i])
				mind=dist[i],minp=i;
		MST+=dist[minp],vis[minp]=true;
		for(EDGE *j=edge[minp];j;j=j->pre)
			if(!vis[j->pnt] && dist[j->pnt]>j->dist)
				dist[j->pnt]=j->dist,pre[j->pnt]=minp;
	}
}

void Mark(int u,int v,bool symble){
	use[u][v]=symble,use[v][u]=symble;
}

void DP(int x,int fa){
	pre[x]=fa;
	for(EDGE *j=edge[fa];j;j=j->pre)
		if(j->pnt==x){
			if(fa!=1) big[x]=max(big[fa],j->dist);
			else big[x]=0;
			break;
		}
	for(EDGE *j=edge[x];j;j=j->pre)
		if(use[x][j->pnt] && j->pnt!=fa) DP(j->pnt,x);
}
	
int main(){
	scanf("%d",&n);
	NameList["Park"]=1;
	for(int i=1;i<=n;i++){
		scanf("%s%s%d",place1,place2,&z);
		addedge(NameOrder(place1),NameOrder(place2),z);
	}
	scanf("%d",&K);
	vis[1]=true;
	for(int i=2;i<=names;i++)
		if(!vis[i]){
			blocks++;
			Count_Connection_Blocks(i);
		}
	memset(vis,false,sizeof(vis));vis[1]=true;
	for(int i=1;i<=blocks;i++) Prim(i);
	for(int i=2;i<=n;i++) Mark(pre[i],i,true);
	memset(Link,0x7f,sizeof(Link));
	for(EDGE *j=edge[1];j;j=j->pre)
		if(Link[belong[j->pnt]]>j->dist)
			Link[belong[j->pnt]]=j->dist,Point[belong[j->pnt]]=j->pnt;
	for(int i=1;i<=blocks;i++){
		MST+=Link[i];
		Mark(1,Point[i],true);
	}
	Degree=blocks;
	addedge(0,1,0);
	DP(1,0);
	while(Degree<K){
		int maxd=0,maxp=0;
		for(EDGE *j=edge[1];j;j=j->pre)
			if(!use[1][j->pnt] && big[j->pnt]-j->dist>maxd)
				maxd=big[j->pnt]-j->dist,maxp=j->pnt;
		if(!maxp) break;
		MST-=maxd,Degree++;
		int i=maxp;EDGE *j;
		while(true){
			for(j=edge[i];j;j=j->pre)
				if(j->pnt==pre[i]) break;
			if(j->dist==big[maxp]) break;
			else i=pre[i];
		}
		Mark(i,pre[i],false);
		pre[maxp]=1;DP(maxp,1);
	}
	printf("Total miles driven: %d\n",MST);
	return 0;
}

  

posted @ 2011-07-28 23:53  Delostik  阅读(386)  评论(0编辑  收藏  举报