7.1 深搜-子集和问题 (枚举子集+剪枝)

---恢复内容开始---

7.1,今日AC题目5到,今天为大家讲解一道普及组的深搜剪枝问题,这道题参解时需要在脑海及对应的手上模拟,切忌眼高手低,切忌三心二意

题目描述

对于一个给定正整数的集合s={x1,x2,x3…xn}和正整数c,编程计算s的第一个子集s1,使得子集s1的和等于c。

 

输入

第一行有2个正整数n和c
第二行有n个正整数
n<7000,c<maxlongint


输出

一行数据,按输入的顺序输出,若无解则输出"No Solution!"
 

样例输入

5 10
2 2 6 5 4

样例输出

2 2 6

题解:
  这道题的基本解题思想是用dfs枚举数组,计算数组的和。
  但若是数据较大耗时较长,我们便须用剪枝来优化,对应的剪枝方法:
    1.计算出后缀和数组sum[i]和后缀最小值数组mina[i]
    2.对于sum[i]数组,在枚举过程中若是当前数组和cnt加上i的后缀和sum[i]小于c,则直接break跳出循环,当前dfs状态结束
    3.对于mina[i]数组,在枚举过程中若是当前数组和cnt加上mina[i]>c,也是直接跳出循环,当前dfs状态结束,跳入下一状态

题解代码:

#include<iostream>
#include<string>
using namespace std;
int n;
long long c,a[7005],sum[7005],mina[7005],ans[7005],minn=0x3f3f3f3f,sum1=0;
bool flag=0;
void dfs(int index1,int index2,int cnt){
 if(cnt>c) return ;
 if(cnt==c){
  for(int i=0;i<index2;i++){
   printf("%lld ",ans[i]);
  }printf("\n");
  exit(0);
 }
 for(int i=index1+1;i<n;i++){
  if(cnt+sum[i]<c) break;
  if(cnt+mina[i]>c) break;
  ans[index2]=a[i];
  dfs(i,index2+1,cnt+ans[index2]);
 }
}
int main(){
 freopen("setsum.in","r",stdin);
 scanf("%d%lld",&n,&c);
 for(int i=0;i<n;i++){
  scanf("%lld",&a[i]);
 }
 for(int i=n-1;i>=0;i--){
  minn=min(minn,a[i]);
  sum1+=a[i];
  sum[i]=sum1;
  mina[i]=minn;
 }
 /*
 for(int i=0;i<n;i++){
  printf("%lld %lld\n",sum[i],mina[i]);
 }
 */
 dfs(-1,0,0);
 printf("No Solution!\n");
 
 return 0;
}


反省与展望:
  7.1日记住这一天,正如孙老师所说:追求卓越,成功便会在不经意间与你相遇
  当今段位usaco青铜,坚持每天进步一点点,哪怕多刷一道题,多学10分钟英语,与樊浩,李俊辉大佬共同进步,争取早日超过孙西越大佬
  争取早日拿下usaco金级段位,信息学竞赛,传统算法高级人才稀缺,高级教育人才有价无市,咸鱼翻身的机会就在眼前,哪怕只有一次机会,都应全力以赴
  如今的我只能辅导入门组,而入门组能辅导的人太多了,争取在暑假尽快提升自我,将普及组的题刷完,辅导普及组,提升自我在第一位,挣钱在第二位,要挣就挣个大的。
  博观而约取,厚积而薄发。

---恢复内容结束---

猜你喜欢

转载自www.cnblogs.com/cxs070998/p/11117549.html