POJ 1639 Picnic Planning 【最小度限制生成树】

<span style="font-size:14px;">原题链接:</span>

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=11062

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


参考资料:

http://blog.csdn.net/ji414341055/article/details/5781800

http://www.cnblogs.com/vongang/archive/2012/07/03/2575383.html

http://blog.sina.com.cn/s/blog_64675f540100sd8m.html

http://wenku.baidu.com/link?url=hUByHi_bxBGW8htMTh3oChXuB-6xAG97SpQXeWtcVR5FjI0DRzkk2AkP2kCptOCm6-rk1SDQdNGVxb31fHVW_i1Sq0fu8dKbk78SxMqlYNy


【思考】{以下不讨论原题,讨论等于K度限制时}

①:如果没有K限制的话,就算你是个233也知道这是个最小生成树=(*@ο@*)=;【什么你居然看不出来(滑稽

②:加了K限制,就有问题了:就是让你生成一棵最小生成树,并且一个点的度为K。

【③(滑稽:什么你看出来这是一道拉板题????【我做时还不知道有板子(这是实话

③:我们把这个东西叫做【度限制最小生成树】(百度拉板不解释


【算法实现】{以下不讨论原题,讨论等于K度限制时}

以下将被限制的点记作0点,其余为1~n,共n+1个点。

①忽视0点的存在。剩下的是一片森林,你可以得到许多连通块,记有m个。

②对于每一个连通块,造一棵最小生成树。

③然后你可以发现,0点到每一个连通块有许多条边,对于每一个连通块,取权值最小的与0点相连。这样我们得到了m度的最小生成树。

④如果m>k,则无解,因为不可能使0点的度小于m。

⑤【以下由m度求m+1度】对于每一条不在生成树内但与0点相连的边,连接后必定有一个环。取环中权值最大但与0点毫无关系的边删除,这样我们可以得到一棵m+1度的树。

⑥为了保证这棵m+1度生成树是最小的,我们要枚举所有不在生成树内但与0点相连的边,取这些树中最小的树。这样一棵m+1度的最小生成树就完成了。

⑦重复⑤⑥,直至m==k时,就造好了一棵k度的最小生成树。


【2333】{让我们回到原题}

题目有我们求小于等于K度。首先我们看到可以看到我们有方法造出m度至k度的最小生成树,取其中最小的一棵输出就好了。


上代码:{POJ 1639}

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <windows.h>
#include <string>
#include <map>

using namespace std;
int n,m,kk,ans,nn,mm,p[200100][2],fa[1010],f[1005][1005];
bool g[1005][1005];
map<string,int> name;

struct poi{
	
	int x,y,num;
	
} haha[200100];

struct QAQ{
	
	int ma,pma;
	
} m_a[1010];

bool cmp(const poi &k,const poi &l){
	
	return k.num<l.num;
	
}

int FF(int k){
	
	while (k!=fa[k]) k=fa[k];
	return k;
	
}

void dfs(int l,int k,int ma,int mak){
	
	fa[k]=l;
	for (int i=1;i<=n;i++)
		if (i!=l&&g[i][k]){
			
			if (f[i][k]>ma){
				
				m_a[i].ma=f[i][k];
				m_a[i].pma=i;
				
			}
			else{
				
				m_a[i].ma=ma;
				m_a[i].pma=mak;
				
			}
			dfs(k,i,m_a[i].ma,m_a[i].pma);
			
		}
	return;
	
}

int getname(string po){
	
	if (name.find(po)==name.end()){
		
		name[po]=++n;
		
	}
	return name[po];
	
}

int main(){
	
	scanf("%d",&nn);m=0;int ll=0;n=0;
	name["Park"]=0;
	for (int i=1;i<=nn;i++){  //读入
		
		string jj,kk;
		int l;cin >> jj >> kk;
		scanf("%d",&l);l++;
		int j=getname(jj),k=getname(kk);
		if (j>k) swap(j,k);
		if (j!=0){
			
			m++;
			haha[m].x=j;haha[m].y=k;haha[m].num=l;
			
		}
		else ll++;
		f[j][k]=f[j][k]==0?l:min(f[j][k],l);
		f[k][j]=f[j][k];
		
	}
	scanf("%d",&kk);
	
	sort(haha+1,haha+m+1,cmp);
	for (int i=1;i<=n;i++) fa[i]=i;
	
	memset(g,0,sizeof(g));
	for (int i=1;i<=m;i++){
		
		int k=FF(haha[i].x),l=FF(haha[i].y);
		if (k==l) continue;
		ans+=haha[i].num;
		g[haha[i].x][haha[i].y]=1;
		g[haha[i].y][haha[i].x]=1;
		fa[k]=l;
		//printf("%d\n",ans);
		
	}
	
	mm=0;
	memset(p,-1,sizeof(p));
	for (int i=1;i<=n;i++){
		
		if (f[0][i]!=0&&(f[0][i]<p[FF(i)][1]||p[FF(i)][1]==-1)){
			
			p[FF(i)][1]=f[0][i];
			p[FF(i)][0]=i;
			
		}
		
	}
	for (int i=1;i<=n;i++){
		
		//printf("%d %d\n",p[i][0],p[i][1]);
		if (p[i][1]>=0){
			
			ans+=p[i][1];
			g[0][p[i][0]]=1;
			g[p[i][0]][0]=1;
			mm++;
			
		}
		
	}
	//printf("%d\n",ans);
	
	int tot=ans;
	for (int i=mm+1;i<=min(kk,ll);i++){
		
		dfs(0,0,-1,0);//father,point,max,maxpoint
		int k=1000000000,l;
		for (int j=1;j<=n;j++)
			if (g[0][j]==0&&f[0][j]>0&&f[0][j]-m_a[j].ma<k){
				
				k=f[0][j]-m_a[j].ma;
				l=j;
				
			}
		ans+=k;
		if (ans<tot) tot=ans;
		g[0][l]=1;g[l][0]=1;
		g[m_a[l].pma][fa[m_a[l].pma]]=0;
		g[fa[m_a[l].pma]][m_a[l].pma]=0;
		//printf("%d %d\n",ans,l);
		
	}
	printf("Total miles driven: %d\n",tot-n);
	system("pause");
	return 0;
	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值