P1460 健康的荷斯坦奶牛
P1460 健康的荷斯坦奶牛
题目:

思路:
-
可以发现,这道题和组合类的题很像
-
先不关注每种饲料分别提供的维他命数,只看序号(1,2,3,4,...,g)
-
题目转换为“求一个组合”,满足
(1)组合所含元素个数最少
(2)字典序最小
-
看题目样例:为了使元素个数少(饲料种数少),很容易想到先用供给维他命很多的种类(e.g. 先用(3)),所以先排序
(1)50 50 50 50 (2)200 300 200 300 (3)900 150 389 399 -
为了使字典序最小,在第一次得出答案后(假设用的饲料种类数为k),还需要继续DFS,看看有没有(同样是用k种饲料),但字典序更小的答案
-
代码解释:
need需要的维他命序列
cans当前所选饲料的编号序列(current-answer)
ans最终输出的饲料编号序列(answer)
cpy[i]记录need[i]变化前的值,利于回溯(必须用局部的数组)按照供给维他命总数降序排序;
组合问题[3,1,2]与[1,2,3]是重复的,因此需要startx标记本轮开始位置
-
另外:
注意控制递归中开辟数组的大小:由于要尽量减少在递归中使用全局变量,局部数组的开辟会浪费很多空间,
DFS中注意控制数组/字符串的赋值次数
代码:
ll v,g,tmp,dep;
struct node{
ll idx,sum;
vector<ll> give;//nd[i]供给维他命的序列
}nd[V];
vector<ll> need,cans(V,0),ans(V,20);
vector<ll> tmpa(V,0);//临时用于排序的数组
bool cmp(node nd1,node nd2)//按照供给维他命总数降序排序
{
return nd1.sum>nd2.sum;
}
//迭代加深
bool dfs(ll startx,ll step)
{
bool flag=false,tag=false;
ll rem=0;
vector<ll> cpy(V,0);
if(step==dep){
for(ll i=0;i<v;++i){
if(need[i]!=0) return false;
}
tmpa=cans;//临时数组 否则排序使cans完全改变 影响回溯
sort(tmpa.begin(),tmpa.begin()+dep);
if(tmpa<ans) ans=tmpa;
return true;
}
for(ll i=startx;i<g;++i){//遍历每一种可能(饲料i)
cans[step]=nd[i].idx;
for(ll j=0;j<v;++j) cpy[j]=need[j];
for(ll j=0;j<v;++j){//第j种维他命
rem=need[j]-nd[i].give[j];
if(rem<0) need[j]=0;
else need[j]=rem;
}
flag=dfs(i+1,step+1);
if(flag) tag=true;
for(ll j=0;j<v;++j) need[j]=cpy[j];
cans[step]=0;
}
return tag;
}
int main()
{
cin>>v;//输入
for(ll i=0;i<v;++i){
cin>>tmp;
need.push_back(tmp);
}
cin>>g;
for(ll i=0;i<g;++i){
nd[i].idx=i;
for(ll j=0;j<v;++j){
cin>>tmp;
nd[i].sum+=tmp;//统计
nd[i].give.push_back(tmp);
}
}
sort(nd,nd+g,cmp);//排序
dep=1;
while(1){//IDDFS
if(!dfs(0,0)) dep++;
else break;
}
cout<<dep<<" ";
for(ll i=0;i<dep;++i) cout<<ans[i]+1<<" ";
return 0;
}

浙公网安备 33010602011771号