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

文章描述了一道使用深度优先搜索(DFS)解决的优化问题,关于如何选择牛饲料以满足特定维他命需求的策略。关键在于递归结束条件的设置和避免重复路径,以找到满足条件的最短饲料组合。代码实现中包含优化技巧,如避免无效搜索和存储最优解。

一道简单的DFS,但是让我写了好久,只能说我是醇比呜呜呜

思路:普通DFS,递归结束的条件是目前选择的牛饲料对应的每种维他命总和满足牛的需求

然后我们需要每次更新最优解

以下是AC代码:

#include<bits/stdc++.h>
using namespace std;
#define TLE ios::sync_with_stdio(0),cin.tie(0)
#define endl "\n"
#define qfor(i,n) for(int i=1; i<=n; i++)
const int N=110;
int g[N][N],v[N];//g[编号为n的饲料][第几种维他命],v[牛所需要的每种维他命目标]
int a,b; //a是维他命数目,b是饲料种树
bool st[N]; //存储转态,是否被标记
int path[N]; //数组存储当前dfs路径存储的答案
int lu[N]; //存储最优解答案
int d=2e5; //存储最优解的长度。因为每次要取min(d,u),所以先给d赋一个很大的值
int w[N]; //存储目前路径每种维他命的总和,判断是否满足牛的需要

bool pan(int x)  //判断目前路径每种维他命的总和,判断是否满足牛的需要
{
    memset(w,0,sizeof(w)); //清空数组先
	for(int j=1; j<=b; j++)
	{
	    for(int i=0; i<x; i++)
        {
            w[j] += g[path[i]][j];
        }
        if(w[j] < v[j])return false; //只要有一种维他命不满足就可以直接return false
	}
	return true; //走完了判断的循环说明这个时候每种维他命都满足牛的需要了
}

void dfs(int u){
    
    if(d <= u)return;  //如果不能更新最优解直接剪枝
    //这里严格小于才更新最优解,因为这样的话就可以保存数字长度一样的时候的第一个解,在字典序中一定是靠前的
    if(u > a)return;  //如果越界了也结束
    
    if(u <= a){  //没越界
        if( pan(u) ){  //这种路径可以满足牛的维他命需求
            d = u;  //更新最优长度
            for (int i = 0; i < d; i ++ )lu[i] = path[i];  //更新最优路径
            return;
        }
    }
    
    for(int r=path[u-1]; r<=a; r++) //这里很重要,决定了是80分还是100分
    //遍历的起点从上一次的编号开始,这样可以避免:1-2-3 和 1-3-2 , 2-1-3 , 2-3-1 , 3-1-2, 3-2-1的重复查找
    //这些重复的选择里面字典序最小的(1-2-3)会被第一个搜出来,其他重复的选择我们不需要,直接不看,这就让某两个TLE的测试点AC了
        if(!st[r]) //这个点没有被标记过
        {
            path[u] = r; //存进路径
            st[r] = true; //标记
            dfs(u+1); //递归找下一层
            
            st[r] = false; //回溯
            path[u] = 0; //回溯
        }
}

int main()
{
    TLE;
    memset(g,0,sizeof(g));
    cin>>b;qfor(i,b)cin>>v[i];
    cin>>a;
    qfor(i,a){
        qfor(j,b)cin>>g[i][j];
    }
    dfs(0); //从0根节点开始深搜
    cout<< d <<" ";  //输出最优解的数字多少
    for (int i = 0; i < d; i ++ ) cout << lu[i] << ' ';  //把存储最优解的数组输出
    cout<<endl;
    
    return 0;
}

tip1.这里的u指的是我们搜到的层数,对应的就是我们选择的饲料的种类多少

tip2.判断递归路径中的答案是否满足递归结束条件的部分我最开始写在了dfs里,并且写的很复杂。其实都可以,并不影响,但是长得很丑QAQ。

  单独开一个函数写,漂亮好多了捏。

tip3.最重要的部分,就是在递归的循环部分的起始点,我最开始是从 1 到 a 遍历一遍找下一个点。但是这样很蠢,因为这样搜到的选择会有大量的重复选择,还是我们不需要的,这样直接让我两个测试点TLE了呜呜呜

tip4.有很多别的写法

tip5.祝看到这段话的你每天AC多多!提前祝你今天开开心心捏0W0

这个问题可以使用**状态压缩 + 枚举子集**的方式来解决。由于饲料种类最多只有 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` 来表示饲料组合,每一位代表是否选择某一种饲料。 - 对每个组合,计算它能提供的每种维他命的总量。 - 如果满足需求,且饲料数量更少,则更新最优解。 - 最终输出饲料数量和编号,按编号升序排列。 --- ###
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值