POJ1639 Picnic Planning 最小度限制生成树

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


【前言】


一道10Y的题目,7WA+2RE。

刚开始确实不知道是这种东西。

后来看了discuss。

后来又下了官方数据。

昨天提交了九次。

到最后熄灯休息。

今天起来A掉。

不过最后一个错误明明是之前有写了的,

不知道怎么就不见了。


【思路】


参考:

(1)http://happylch21.blog.163.com/blog/static/16563975920113224491211/

(2)http://wenku.baidu.com/view/f186b1daa58da0116c1749ac.html

(3)http://hi.baidu.com/czyuan_acm/blog/item/5b832926f9910f108a82a1fb.html


度限制生成树是NP Hard问题,但是由于这道题只限制一个点,所以可解。

下面介绍计算过程:

(1)把根结点v0去掉,同时去掉跟其相连的边。

(2)计算此时的连通个数m,并分别求出每个连通分量的最小生成树。

(3)计算每个连通分量到v0的最小权值边,并将此边添加进来,于是添加了m条边。

(4)限制度为k。如果k==m,说明此时已满足条件,退出。

(5)如果k<m,说明无解,退出。

(6)如果k>m,则接下来就是要从m度增加到m+1度。

(7)查看所有不在当前树中的与v0有关联的边,计算:如果将其添加进来,必定存在一个环,计算此环上与v0不相连的最大权值的边,并计算该边减去与环相连的边的值(可用DP优化)。找出所有这样的最大值,找出其中最大的,将该边从当前树中去掉,并把该条与v0相连的边添加到当前树中。

(8)如果已无法找到边,或者找到的边j减去连边的值小于等于零,即即使添加了权值总和也不会减少,则退出。

(9)最终得到k度限制最小生成树。


顺便介绍次小生成树:

求出最小生成树。

枚举所有不在树中的边,如果将其添加进来则必定存在一个环,找出环上的最大权值边,并计算连边与该边的差。

找出所有这样结果中的最小值,并把相应的边删去而把连边添加进来。

便可得到次小生成树。


【代码】


能力不足,代码200+,而且似乎无优化= =


#include <iostream>
#include <map>
#include <string>
using namespace std;

const int maxn = 20;
const int MAX = 99999999;

int g[maxn+5][maxn+5];
int f[maxn+5][maxn+5];
int visited[maxn+5];
int edge[maxn+5][maxn+5];

struct detail
{
	int u;
	int v;
	int w;
	int jv;
	int jw;
	int add;
};

void prim(int v, int n, int root, int &sum)
{
	int i, j;
	int temp, tb, tp;
	int d[maxn+5];
	int ct = 1;
	d[0] = v;
	visited[v] = 1;
	while(1)
	{
		temp = MAX;
		for (i=0; i<ct; i++)
		{
			for (j=0; j<n; j++)
			{
				if (visited[j]==0 && f[d[i]][j]<temp)
				{
					temp = f[d[i]][j];
					tb = d[i];
					tp = j;
				}
			}
		}
		if (temp==MAX) break;
		sum += temp;
		visited[tp] = 1;
		d[ct] = tp;
		ct++;
		edge[tb][tp] = edge[tp][tb] = 1;
	}
	temp = MAX;
	for (i=0; i<ct; i++)
	{
		if (g[root][d[i]]<temp)
		{
			temp = g[root][d[i]];
			tp = d[i];
		}
	}
	f[root][tp] = f[tp][root] = temp;
	edge[root][tp] = edge[tp][root] = 1;
	sum += temp;
}


int q[maxn*maxn];
int pre[maxn*maxn];

detail bfs(int v, int root, int n)
{
	detail temp;
	int i;
	int t;
	int head = 1;
	int tail = 0;
	q[0] = v;
	int sign[maxn+5] = {0};
	sign[v] = 1;
	pre[v] = -1;
	while(head>tail)
	{
		t = q[tail];
		tail++;
		for (i=0; i<n; i++)
		{
			if (sign[i]==0 && edge[t][i]==1 && f[t][i]<MAX)
			{
				sign[i] = 1;
				pre[i] = t;
				q[head] = i;
				head++;
				if (i==root)
					break;
			}
		}
		if (i<n) break;
	}
	if (head==tail)
	{
		temp.add = -MAX + 1;
	}
	else
	{
		temp.w = -MAX;
		while(pre[i]!=-1)
		{
			if (f[i][pre[i]]>temp.w)
			{
				temp.w = f[i][pre[i]];
				temp.u = pre[i];
				temp.v = i;
			}
			i = pre[i];
		}
		temp.jv = v;
		temp.jw = g[v][root];
		temp.add = temp.w - temp.jw;
	}
	return temp;
}

int MST(int n, int v, int k)
{
	int sum = 0;
	int i, j;
	for (i=0; i<n; i++)
	{
		for (j=0; j<n; j++)
		{
			f[i][j] = g[i][j];
			edge[i][j] = 0;
		}
	}
	for (i=0; i<n; i++)
		f[v][i] = f[i][v] = MAX;
	for (i=0; i<n; i++)
		visited[i] = 0;
	visited[v] = 1;
	int ct = 0;
	for (i=0; i<n; i++)
	{
		if (visited[i]==0)
		{
			prim(i, n, v, sum);//查找连通量
			ct++;
		}
	}
	detail temp, md;
	for (i=ct; i<k; i++)//循环增删边
	{
		md.add = -MAX;
		for (j=0; j<n; j++)
		{
			if (edge[v][j]==0 && g[v][j]<MAX)
			{
				temp = bfs(j, v, n);//查找最大权值边
				if (temp.add>md.add)
					md = temp;
			}
		}
		if (md.add<=0) break;//如果无法再减少权值和则退出
		f[v][md.jv] = f[md.jv][v] = g[v][md.jv];
		edge[v][md.jv] = edge[md.jv][v] = 1;
		f[md.u][md.v] = f[md.v][md.u] = MAX;
		edge[md.u][md.v] = edge[md.v][md.u] = 0;
		sum -= md.add;
	}
	return sum;
}

int main()
{
	map<string,int>m;
	int n;
	int i, j;
	int s;
	string x, y;
	int tx, ty;
	int dist;
	m.clear();
	for (i=0; i<=maxn; i++)
	{
		for (j=0; j<=maxn; j++)
			g[i][j] = MAX;
	}
	cin>>n;
	for (i=0; i<n; i++)
	{
		cin>>x>>y>>dist;
		if (m.find(x)==m.end())
			m[x] = m.size();
		if (m.find(y)==m.end())
			m[y] = m.size();
		tx = m[x];
		ty = m[y];
		if (dist<g[tx][ty])
			g[tx][ty] = g[ty][tx] = dist;
	}
	cin>>s;
	cout<<"Total miles driven: "<<MST(m.size(), m["Park"], s)<<endl;
	return 0;
}


【P.S】


相信自己,勇往直前!




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值