【珠宝】解题报告

                                 珠宝(GEMS) 

    给一棵n 个结点的树,给每个点安排一个正整数编号,使得相邻点具有不同的编号,编号的 

总和尽量小。 

输入文件: 

  第一行:n(n<=50,000) 

  以下n-1 行,每行两个数u,v(1<=u,v<=n),表示u          和v 有一条边 

输出文件: 

  仅一行,为最小编号和 

SAMPLE INPUT 

8 

1  2 

1  3 

1  4 

1  5 

5  6 

5  7 

5  8 

SAMPLE OUTPUT 

11 

这道题我基本思路+骗分70分。


思路和正确的接近:

从叶节点开始,向根。叶节点尽量小。(*)

因此我想到了拓扑排序(不过因为是从叶节点开始,所以入度和初度交换了,感觉不是很科学),

节点“入度”为0的时刻即为他的编号。(这点其实是错误的,想得出反例)


第一次我用的栈维护的堆,后来因为一些自己做的测试数据有问题,因此改成了用队列维护。

结果过不了样例,我还是交了,居然70分。。(用栈的版本10分)

//=========================================================================

正确的思路:

从(*)处不同。

从(*)处想到后序遍历,因此可以想到树形DP,


引用:

看完题目,很自然地联想到树型DP,而且也不难得到状态表示方法。先根据输入的数据构造出一棵树,然后从树的叶子结点往上倒推。设F[I,J]表示以I为根的子树在I的编号为J时,可以得到的最小编号和。

状态转移方程为:F[I,J] = Min{∑F[I1, J1]} + J

其中I1表示I的一个子结点编号,J1为不同于J的一个自然数。


(这道题不能用四色定理,不知道为什么?因此J不能只开到4,开大一点20比较安全)

动规之上还必须用优化才能AC。。。。。。。。。。

//==========================================================================

骗分程序

//贪心 

#include <iostream>
using std::cout;
//using std::cin;
#include <cstdio>
#include <cstdlib>
const long oo = 0x7fff0000;

struct ftv
{
	long f;
	long t;
};

long n;
ftv bian[100002];
long chudu[50002];
long start[50002];

long top = 0;
long cengci[50002];

long que[100002];
long l=0;long r=0;

long long tot=0;

int bigger(const void* a,const void* b)
{
	ftv* aa = (ftv*) a;
	ftv* bb = (ftv*) b;
	long aaa = aa->f;
	long bbb = bb->f;
	if (aaa>bbb) return 1;
	else if(aaa<bbb) return -1;
	else return -1;
}

int main()
{
	freopen("gems.in","r",stdin);
	freopen("gems.out","w",stdout);
	
	scanf("%ld",&n);
	
	long t = 0;
	for (long i=1;i<n;i++)
	{
		long u;long v;
		scanf("%ld%ld",&u,&v);
		t++;
		bian[t].f = u;
		bian[t].t = v;
		t++;
		bian[t].t = u;
		bian[t].f = v; 
		chudu[bian[t].f]++;
		chudu[bian[t].t]++;
	}
	long m = (n-1)*2;
	qsort(bian+1,m,sizeof(ftv),&bigger);
	for (long i=1;i<=m;i++)
	{
		if (start[bian[i].f]==0)
			start[bian[i].f]=i;
	}
	
	for (long i=1;i<n+1;i++)
	{
		if (chudu[i]==1)
		{
			que[++r] = i;
			cengci[i] = 1;
			tot++;
		}
	}
	
	while (l<r)
	{
		long now = que[++l];
		chudu[now]=-1;
		for (long i=start[now];i<=m;i++)
		{
			if (bian[i].f!=now) break;
			if (chudu[bian[i].t]>1)
			{
				chudu[bian[i].t]--;
				if (cengci[bian[i].t]<cengci[now]+1)
				{
					cengci[bian[i].t]=cengci[now]+1;
				}
				if (chudu[bian[i].t]==1)
				{
					tot += cengci[bian[i].t];
					que[++r] = bian[i].t;
				}
			}
		}
	}
	cout << tot;
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值