放置街灯(UVA 10859)

树形DP解决照明问题
本文介绍了一种使用树形动态规划方法解决特定照明问题的算法。问题要求在一个无向无环图中放置最少数量的灯,使得所有边都能被至少一个灯照亮,并最大化被两个灯同时照亮的边的数量。文章详细解释了状态转移方程的设计思路,并提供了一个具体的实现代码。

问题描述

给你一个n个点m条边的无向无环图,在尽量少的节点上放灯,使得所有灯都被照亮。每盏灯将照亮以它为一个端点的所有边。在灯的总数最小的前提下,被两盏灯同时照亮的边数应尽量大。

输入格式

输入的第一行为测试数据组数T(T≤30)。每组数据第一行为两个整数n和m(m<n≤1000),即点数(所有点编号为0~n-1)和边数;以下m行每行为两个不同的整数a和b,表示有一条边连接a和b(0≤a,b≤n)。

输出格式

对于每组数据,输出3个整数,即灯的总数,被2个灯照亮的边数和只被一个灯照亮的边数。

分析

因为是无向无环图(即森林),所以这道题是树上的动态规划。首先,要先知道一点:对于2个变量a,b,如果要在a最小的前提下,求b的最小值,可以求x=M*a+b的最小值,那么最后答案即为min(a)=x/M,min(b)=x%M,其中M值要大于a的理论最大值与b的理论最小值之差。道理很简单,当M足够大的时候,a决定整个式子的值,而b的值对式子的值不会造成多大的影响,只有当a值不变时,b才能对式子的值造成影响。

那么,对于这道题来说,有2个待优化的条件:1、灯的总数a最小;2、在1的前提下,被2个灯照亮的边数b最大。一个最大,一个最小,显然不方便优化,所以在上面结论的提示下,考虑将第二个条件等价为被1个灯照亮的边数c最小(一条边只能同时被一盏或者2盏灯照亮),这时候就可以直接套用上面的结论了。设x=M*a+c,这里M可以取2000,或者更大,但是要注意不能太大,否则在运算时可能会造成数据溢出。

分析到这里,动态规划的状态方程也不难想了,因为对于每个节点i来说,只有2种方案,放灯或者不放灯,但是节点i放灯与否与它的父节点也有关系,所以设d(i,j)为节点i的父节点j是否放灯(j值为1是放灯,0是不放)的最小x值。下面分别对2种方案进行分析:

1、节点i不放灯。那么i的父节点必须放灯(j=1)或者i本身是根节点。此时d(i,j)=sum{d(k,0)|k取遍i的所有子节点},如果i不是根,还要加上1,因为节点i与其父节点这条边上只有1盏灯

2、节点i放灯。此时d(i,j)=sum{d(k,1)|k取遍i的所有子节点}+M,同样的,如果j=0,且i不是根,还得加上1,因为节点i与其父节点这条边上只有1盏灯。

  

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

vector<int> adj[1010];
int vis[1010][2], d[1010][2], n, m;
int dp(int i, int j, int f) {
	if (vis[i][j])
		return d[i][j];
	vis[i][j] = 1;
	int& ans = d[i][j];

	ans = 2000;
	for (int k = 0; k < adj[i].size(); k++) {
		if (adj[i][k] != f) {
			ans += dp(adj[i][k], 1, i);
		}
	}
	if (!j&&f >= 0)
		ans++;

	if (j || f < 0) {
		int sum = 0;
		for (int k = 0; k < adj[i].size(); k++)
			if (adj[i][k] != f)
				sum += dp(adj[i][k], 0, i);
		if (f >= 0)
			sum++;
		ans = min(ans, sum);
	}
	return ans;
}

int main() {
	int T, a, b;
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &m);
		for (int i = 0; i < n; i++) {
			adj[i].clear();
		}
		for (int i = 0; i < m; i++) {
			scanf("%d%d", &a, &b);
			adj[a].push_back(b);
			adj[b].push_back(a);
		}
		memset(vis, 0, sizeof(vis));
		int ans = 0;
		for (int i = 0; i < n; i++) {
			if (!vis[i][0])
				ans += dp(i, 0, -1);
		}
		printf("%d %d %d\n", ans / 2000, (m - ans) % 2000, ans % 2000);
	}
	return 0;
}


MATLAB代码实现了一个基于多种智能优化算法优化RBF神经网络的回归预测模型,其核心是通过智能优化算法自动寻找最优的RBF扩展参数(spread),以提升预测精度。 1.主要功能 多算法优化RBF网络:使用多种智能优化算法优化RBF神经网络的核心参数spread。 回归预测:对输入特征进行回归预测,适用于连续值输出问题。 性能对比:对比不同优化算法在训练集和测试集上的预测性能,绘制适应度曲线、预测对比图、误差指标柱状图等。 2.算法步骤 数据准备:导入数据,随机打乱,划分训练集和测试集(默认7:3)。 数据归一化:使用mapminmax将输入和输出归一化到[0,1]区间。 标准RBF建模:使用固定spread=100建立基准RBF模型。 智能优化循环: 调用优化算法(从指定文件夹中读取算法文件)优化spread参数。 使用优化后的spread重新训练RBF网络。 评估预测结果,保存性能指标。 结果可视化: 绘制适应度曲线、训练集/测试集预测对比图。 绘制误差指标(MAE、RMSE、MAPE、MBE)柱状图。 十种智能优化算法分别是: GWO:灰狼算法 HBA:蜜獾算法 IAO:改进天鹰优化算法,改进①:Tent混沌映射种群初始化,改进②:自适应权重 MFO:飞蛾扑火算法 MPA:海洋捕食者算法 NGO:北方苍鹰算法 OOA:鱼鹰优化算法 RTH:红尾鹰算法 WOA:鲸鱼算法 ZOA:斑马算法
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值