头像

欢迎来到我的博客

分享题解与总结

P1460 健康的荷斯坦奶牛

P1460 健康的荷斯坦奶牛

题目:

image

思路:

  1. 可以发现,这道题和组合类的题很像

  2. 先不关注每种饲料分别提供的维他命数,只看序号(1,2,3,4,...,g)

  3. 题目转换为“求一个组合”,满足

    (1)组合所含元素个数最少

    (2)字典序最小

  4. 看题目样例:为了使元素个数少(饲料种数少),很容易想到先用供给维他命很多的种类(e.g. 先用(3)),所以先排序

    (1)50  50  50  50
    (2)200 300 200 300
    (3)900 150 389 399
    
  5. 为了使字典序最小,在第一次得出答案后(假设用的饲料种类数为k),还需要继续DFS,看看有没有(同样是用k种饲料),但字典序更小的答案

  6. 代码解释:

    need需要的维他命序列

    cans当前所选饲料的编号序列(current-answer)

    ans最终输出的饲料编号序列(answer)

    cpy[i]记录need[i]变化前的值,利于回溯(必须用局部的数组)

    按照供给维他命总数降序排序;

    组合问题[3,1,2]与[1,2,3]是重复的,因此需要startx标记本轮开始位置

  7. 另外:

    注意控制递归中开辟数组的大小:由于要尽量减少在递归中使用全局变量,局部数组的开辟会浪费很多空间,

    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;
}
posted @ 2026-04-03 20:57  king_steph1209  阅读(15)  评论(0)    收藏  举报