算法刷题记录(Day 54)

这篇博客探讨了两种树结构的处理问题。第一题是ContestantsDivision,要求通过深度优先搜索找到能将树分成两个部分,使得节点值之差最小的方案。第二题是TheLostHouse,涉及树的遍历和路径优化,考虑虫子的影响。文章介绍了如何使用深度优先搜索和贪心策略来解决这两个问题,并提供了详细的解题代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Contestants Division(poj 3140)

题目类型:深度优先搜索
原题链接
题目大意:给出一棵树的结构,每个节点都对应着一个值。选择一条边,将该树分为两个部分,使得两个部分的节点和之差最小。并输出差的最小值。
在这里插入图片描述
在这里插入图片描述
虽然M的范围比N大,但是根据题意,形成的是一棵树,因此M=N-1。

解题思路:
使用类似于线段树的思想,每一个树节点维护一个变量,为以该节点为根节点的子树的节点数x。那么断开其和父亲的连接,就会形成两棵子树,分别为x和S-x。
那么最终是一个深度优先遍历的过程,实践复杂度为0(N)

#include<iostream>
#include<algorithm>
using namespace std;
#define NMAX 100005
int N, M;
long long num[NMAX];
int is[NMAX];
long long sum[NMAX];
long long res = 0x3f3f3f3f3f3f3f3f;
struct Edge {
	int from, to, nxt;
}E[2*NMAX];
int head[NMAX], cnt = 1;
long long S = 0; //注意这个也应该是long long类型
void add(int s, int t) {
	E[cnt].from = s, E[cnt].to = t, E[cnt].nxt = head[s];
	head[s] = cnt++;

	E[cnt].from = t, E[cnt].to = s, E[cnt].nxt = head[t];
	head[t] = cnt++;
}
void dfs(int u) {
	is[u] = 1;
	sum[u] = num[u];
	for (int i = head[u]; i; i = E[i].nxt) {
		int v = E[i].to;
		if (!is[v]) {
			dfs(v);
			sum[u] += sum[v];
		}
	}
	if (u != 1) {
		long long sub = sum[u] - (S - sum[u]);
		if (sub < 0) sub = -sub;
		if (sub < res) res = sub;
	}
}
int main() {
	int c = 1;
	while (1) {
		scanf_s("%d%d", &N, &M);
		if (!N && !M) break;
		memset(head, 0, sizeof(head));
		memset(is, 0, sizeof(is));
		memset(sum, 0, sizeof(sum));
		cnt = 1;
		S = 0;
		for (int i = 1; i <= N; i++) {
			scanf_s("%lld", &num[i]);
			S += num[i];
		}
		for (int i = 0; i < M; i++) {
			int s, t;
			scanf_s("%d%d", &s, &t);
			add(s, t);
		}
		res = S;
		dfs(1);
		printf("Case %d: %lld\n",c++, res);
	}
}

tip:
1.注意数据类型的表示,不要弄错了

The Lost House(poj 2057)

原题链接
题目大意:一棵具有N(1000)个节点的树,节点上可能会有一个虫子。对于一种遍历(指的是遍历所有的叶子节点),求其路径长度的最小值。由于虫子的存在,会使得总的路径长度发生变化。同时,需要注意到,各个节点的遍历顺序会直接影响到总的路径长度。
限定在一个所谓的爬行顺序上可能会有局限

//初步实现->WA->没有认识到本质
#include<iostream>
using namespace std;
#define NMAX 1003
int N;
struct Edge {
	int from, to, nxt;
}E[NMAX];
int head[NMAX], cnt = 1, worm[NMAX];
int dp[NMAX][3];
void add(int u, int v) {
	E[cnt].from = u, E[cnt].to = v, E[cnt].nxt = head[u];
	head[u] = cnt++;
}
void dfs(int u) {

	int j = 1;
	int k = 0;
	for (int i = head[u]; i; i = E[i].nxt) {
		int v = E[i].to;
		dfs(v);
		dp[u][2] += dp[v][2];
		if (!worm[v]) dp[u][0] += (2 + dp[v][0]);
		else dp[u][0] += 2;
		if (!worm[v]) dp[u][1] += (j*dp[v][2] + dp[v][0] + dp[v][1]);
		else dp[u][1] += (j*dp[v][2] + dp[v][1]); //需要对dp[v][2]进行排序,且有虫的必须位于前面
		j += 2;
		if (dp[v][0] > k) k = dp[v][0];
	}
	if (dp[u][2]) dp[u][1] -= k;
	else dp[u][2] = 1;//代表的是叶子节点
}
int main() {
	while (1) {
		scanf_s("%d", &N);
		memset(head, 0, sizeof(head));
		memset(worm, 0, sizeof(worm));
		memset(dp, 0, sizeof(dp));
		cnt = 1;
		for (int i = 1; i <= N; i++) {
			int fa;
			char c;
			scanf_s("%d %c", &fa, &c);
			if (fa != -1) add(fa, i);
			if (c == 'Y') worm[i] = 1;
			else if (c == 'N') worm[i] = 0;
			else cout << "WRONG" << endl;
		}
		dfs(1);
		printf("%.4f\n", (double)dp[1][1] / dp[1][2]);
	}
}

关于全排序中的贪心问题
在这里插入图片描述

参考链接

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define NMAX 1003
int N;
struct Edge {
	int from, to, nxt;
}E[NMAX];
int head[NMAX], cnt = 1, worm[NMAX];
int dp[NMAX][3];
void add(int u, int v) {
	E[cnt].from = u, E[cnt].to = v, E[cnt].nxt = head[u];
	head[u] = cnt++;
}
void dfs(int u) {
	vector<pair<double, int>> xu;
	int j = 1;
	int k = 0;
	for (int i = head[u]; i; i = E[i].nxt) {
		int v = E[i].to;
		dfs(v);
		dp[u][2] += dp[v][2];
		if (!worm[u]) dp[u][0] += (2 + dp[v][0]);
		xu.push_back(make_pair((double)(dp[v][0]+2)/dp[v][2],v));
	}
	//若(u,v)则house在v上应为dp[v][2]*(k+1)+dp[v][1]  k为不在前面的所有的路程 k=sum(dp[vi][0]+2)
	//对于序列x1,x2,x3,...xi,xj...交换xi和xj,
	//按照(dp[xi][0]+2)/dp[xi][2]从小到大的顺序
	if (dp[u][2]) {
		sort(xu.begin(), xu.end());
		long long ssum = 0;
		for (int i = 0; i < xu.size(); i++) {
			int v = xu[i].second;
			dp[u][1] += (dp[v][2] * (ssum+1) + dp[v][1] );
			ssum += (dp[v][0] + 2);
		}
	}
	else dp[u][2] = 1;//代表的是叶子节点
}
int main() {
	while (1) {
		scanf_s("%d", &N);
		if (!N) break;
		memset(head, 0, sizeof(head));
		memset(worm, 0, sizeof(worm));
		memset(dp, 0, sizeof(dp));
		cnt = 1;
		for (int i = 1; i <= N; i++) {
			int fa;
			char c;
			scanf_s("%d %c", &fa, &c);
			if (fa != -1) add(fa, i);
			if (c == 'Y') worm[i] = 1;
			else if (c == 'N') worm[i] = 0;
			else cout << "WRONG" << endl;
		}
		dfs(1);
		printf("%.4f\n", (double)dp[1][1] / dp[1][2]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值