洛谷P1460 [USACO2.1]健康的荷斯坦奶牛 Healthy Holsteins

使用DFS解决二维数组优化问题
这篇博客主要介绍了如何利用深度优先搜索(DFS)解决一个关于饲料分配的问题。作者通过编写代码并详细注释,解释了如何巧妙地定义DFS函数参数以降低复杂度,并讨论了在DFS中进行标记和状态判定的重要性。在代码实现过程中,作者设置了visit和Anvisit数组跟踪饲料编号,以及一个ans变量用于存储最小饲料数量。最后,通过DFS遍历所有可能的饲料组合,判断是否满足需求并更新答案。

传送门:https://www.luogu.com.cn/problem/P1460

写这道题题解是因为对于我对题目的理解是对的,思路也比较清晰。但是在DFS代码的技巧上有欠缺导致无法写出完全AC的代码。从题解中我对DFS函数定义的参数列表有了进一步的了解,在DFS算法中,对与参数列表的定义也是极为重要的,有技巧性地定义参数列表可以减小代码的复杂度,也让写题思路更加清晰。

同时,对与DFS的标记访问和去除标记也清晰了一些,比第一天刚开始摸索时不知道从何下手进步了不少,继续加油!

言归正传。这道题目还是一个两路搜索,对于每种饲料,都可以选择加或者不加。同时还需要设置visit数组来保存访问路径。每访问后需要对此时的状态进行判定,是否满足条件,满足条件则返回并对比答案来确定是否要更新答案,不满足条件继续向下搜索。

上代码,注释写得还算详细。

#include<iostream>
using namespace std;
int Vitamin[30][30] = { 0 }, Need[30] = { 0 };
//两个数组分别是饲料各维他命含量,牛所需维他命量
int visit[30] = { 0 }, Anvisit[30] = { 0 }, ans = 5000;
//visit用来存放当前饲料编号,Anvisit存放答案的饲料编号
//ans存放答案数量,初始值为一个比较大的数
int v = 0, g = 0;
//v,g含义题目中有,不赘述
bool Judge(int amount)
{
	//该函数用来判断当前情况下是否已满足需求
	//amount是已投喂的饲料总数
	if (!amount)return false;
	//如果一种饲料都没加直接pass
	for (int i = 1; i <= v; i++)
	{
		int sum = 0;
		for (int j = 1; j <= amount; j++)
			sum += Vitamin[visit[j]][i];
		//累加,算出每种维生素的含量
		if (sum < Need[i])return false;
		//一旦出现有一种含量小于需求的直接false
	}
	return true;
}
void DFS(int number,int count)
{
	//number为饲料编号,count为饲料总数
	//这样设置参数可以很大程度上避免思考得过于复杂
	if (number > g)
		return;
	//边界
	if (Judge(count)) {//如果满足条件
		if (count < ans) {//并且新答案比原答案小
			ans = count;
			for (int i = 1; i <= count; i++)
				Anvisit[i] = visit[i];
			//更新答案
		}
		return;
		//使命完成,直接返回,不需要再向下执行
	}
	visit[count + 1] = number + 1;
	//准备访问,先打标记
	DFS(number + 1, count + 1);
	//访问下一种饲料并加入
	visit[count + 1] = 0;
	//访问结束,消除标记
	DFS(number + 1, count);
	//访问下一种饲料但不加入
}
int main(void)
{
	scanf("%d", &v);
	for (int i = 1; i <= v; i++)
		scanf("%d", &Need[i]);
	scanf("%d", &g);
	for (int i = 1; i <= g; i++)
		for (int j = 1; j <= v; j++)
			scanf("%d", &Vitamin[i][j]);
	//一堆输入
	DFS(0, 0);
	//注意从编号零开始搜索,因为编号一也要搜索加或者不加两条路
	printf("%d", ans);
	for (int i = 1; i <= ans; i++)
		printf(" %d", Anvisit[i]);
	return 0;//完结撒花
}

 

这个问题可以使用**状态压缩 + 枚举子集**的方式来解决。由于饲料种类最多只有 15 种,因此我们可以枚举所有可能的饲料组合(最多 $2^{15} = 32768$ 种),然后判断每种组合是否能满足牛的维他命需求。 --- ### 解题思路: 1. 枚举所有饲料组合(从 0 到 $2^g - 1$)。 2. 对于每个组合,累加每种饲料提供的维他命量。 3. 判断是否满足牛的最低维他命需求。 4. 如果满足,记录该组合的饲料数量和具体编号。 5. 最终找出饲料数量最少的组合,如果有多个解,选择字典序最小的。 --- ### C++代码实现如下: ```cpp #include <iostream> #include <vector> #include <algorithm> #include <climits> using namespace std; int main() { int v; cin >> v; vector<int> required(v); for (int i = 0; i < v; ++i) { cin >> required[i]; } int g; cin >> g; vector<vector<int>> feeds(g, vector<int>(v)); for (int i = 0; i < g; ++i) { for (int j = 0; j < v; ++j) { cin >> feeds[i][j]; } } int min_feed_count = INT_MAX; vector<int> best_combination; // 枚举所有饲料组合 for (int mask = 1; mask < (1 << g); ++mask) { vector<int> total(v, 0); vector<int> selected; for (int i = 0; i < g; ++i) { if (mask & (1 << i)) { selected.push_back(i); for (int j = 0; j < v; ++j) { total[j] += feeds[i][j]; } } } bool valid = true; for (int j = 0; j < v; ++j) { if (total[j] < required[j]) { valid = false; break; } } if (valid && selected.size() < min_feed_count) { min_feed_count = selected.size(); best_combination = selected; } } // 输出结果 sort(best_combination.begin(), best_combination.end()); cout << min_feed_count; for (int idx : best_combination) { cout << " " << idx + 1; // 输出编号从1开始 } return 0; } ``` --- ### 代码解释: - 使用位掩码 `mask` 来表示饲料组合,每一位代表是否选择某一种饲料。 - 对每个组合,计算它能提供的每种维他命的总量。 - 如果满足需求,且饲料数量更少,则更新最优解。 - 最终输出饲料数量和编号,按编号升序排列。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值