传送门: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;//完结撒花
}