部分和问题
-
描述
-
给定整数a1、a2、.......an,判断是否可以从中选出若干数,使它们的和恰好为K。
-
输入
-
首先,n和k,n表示数的个数,k表示数的和。
接着一行n个数。
(1<=n<=20,保证不超int范围)
输出
- 如果和恰好可以为k,输出“YES”,并按输入顺序依次输出是由哪几个数的和组成,否则“NO” 样例输入
-
4 13 1 2 4 7
样例输出
-
YES 2 4 7
来源
- 经典题目
-
首先,n和k,n表示数的个数,k表示数的和。
这道题是水题?
我咋觉得一点也不水~~~~~
可能是我太水,我自己写的代码 发现了很多问题,结果都出现不了 下面我一点一点分析
#include<stdio.h>
#include<string.h>
bool flag;
int a[30],b[30],sum;
int n,k,j;
void dfs(int x)
{
sum+=a[x];
if(sum==k)
{
flag=true;
return;
}
if(sum>k)
{
sum-=a[x];
return;
}
if(sum<k)
{
b[j++]=a[x];
dfs(x+1);
}
}
int main()
{
while(scanf("%d%d",&n,&k)!=EOF)
{
j=0,sum=0;
memset(b,0,sizeof(b));
for(int i=0; i<=n; i++)
{
scanf("%d",&a[i]);
}
for(int i=0; i<=n; i++)
{
dfs(i);
}
if(flag)
{
printf("YES\n");
for(int i=0; a[i]!=0; i++)
{
printf("%d ",b[i]);
}
printf("\n");
}
else
{
printf("NO\n");
}
}
}
这是我第一次的代码 都无法输出结果
递归都写错 了 几乎没有一点对的(初学见谅)
原先的思路是把数存进b【30】中 但是发现麻烦 不如直接判断这个数用没,没用表示0 用了表示1
而且这样写递归 不容易看对错
还有一个问题 我在递归里面没有用for循环,代替的在主函数里用了
(说说for循环的作用);如果没有for循环 那么我只是简单递归 这个数加不加 如样例 1 2 4 7 先1 接着递归 2 继续 4 还小于13 继续7 变成14 好了不满足条件
我返回 只有 1 2 4 根据代码 我继续返回 1 2 继续返回 1 最后没了 那就没答案了
但是 我for循环加在主函数后 只是在 第一个数选择 1不行时选择 第一个数为2 选择时 中间不会跳过数字 如 4 17 1 7 3 10 这组样例 1开头不行 我7开头 递归3 不够 递归 10 多了 返回 再返回 最后又是无解
for在里面就没有这种情况
for(int i=pos;i<n;i++)
{
sum+=a[i];
visit[i]=1;
dfs(i+1);
sum-=a[i];
visit[i]=0;
}
最后修改 成这样
#include <cstdio>
#include <cstring>
using namespace std;
int a[30],visit[30],n,k,sum;
bool flag;
void dfs(int pos)
{
if(flag==true)
return ;
if(sum>k)
{
return;
}
if(sum==k)
{
flag=true;
return;
}
for(int i=pos; i<n; i++)
{
sum+=a[i];
visit[i]=1;
//printf("%d %d %d\n",pos,i,sum);
/*for(int k=0; k<n; k++)
{
printf("%d ",visit[k]);
}
printf("\n");*/
dfs(i+1);
if(!flag)
{
sum-=a[i];
visit[i]=0;
}
}
}
int main()
{
int i;
while(scanf("%d%d",&n,&k)!=EOF)
{
for(i=0; i<n; i++)
scanf("%d",&a[i]);
memset(visit,0,sizeof(visit));
flag=false;
dfs(0);
if(!flag)
printf("NO\n");
else
{
printf("YES\n");
for(int j=0; j<n; j++)
{
if(visit[j]==1)
{
printf("%d ",a[j]);
}
}
}
}
return 0;
}
但是还是有问题 样例能过 但是WA
下面测试一组数据
5 9
1 2 3 4 10
输出结果竟然是
2 3 4 10
明显 10时多余的
为啥??????
if(!flag)
{
sum-=a[i];
visit[i]=0;
}
不知道注意到这没
我是在主函数外判断flag 再输出结果
但是有一个问题 我递归的时候 最后递归结束的时候 会把我做的标记全部消除····
最后只知道YES 没法知道值
为了不让最后递归的时候 把我标记全清零
那么我记 如果找到答案了 我就回溯的时候就不再清我的标记
但是 又出现个小尾巴 ,我递归10时把10标记了
但是我找到答案了 又没有消除!!!那么成小尾巴了
怎么办 那我只好找到答案直接赶紧趁热 输出
要不递归问题多
void dfs(int pos)
{
if(flag==true)
{
printf("YES\n");
for(int j=0; j<n; j++)
{
if(visit[j]==1)
{
printf("%d ",a[j]);
}
}
return ;
}
if(sum>k)
{
return;
}
if(sum==k)
{
flag=true;
return;
}
for(int i=pos; i<n; i++)
{
sum+=a[i];
visit[i]=1;
dfs(i+1);
if(!flag)
{
sum-=a[i];
visit[i]=0;
}
}
}
只把递归函数变了
哈哈哈 没问题了~!!!
求豆麻袋 输出的结果由于我递归回去的时候 flag=true 一直成立 我就会输出很多组值~~~~~~
天天天天啊~~~~
再想 我让以第一次跟后面的都不一样
#include <cstdio>
#include <cstring>
using namespace std;
int a[30],visit[30],n,k,sum;
bool flag;
void dfs(int pos)
{
if(sum==k)
{
flag=true;
printf("YES\n");
for(int j=0; j<n; j++)
{
if(visit[j]==1)
{
printf("%d ",a[j]);
}
}
printf("\n");
return ;
}
if(sum>k)
{
return;
}
for(int i=pos; i<n; i++)
{
sum+=a[i];
visit[i]=1;
printf("%d %d %d\n",pos,i,sum);
for(int k=0; k<n; k++)
{
printf("%d ",visit[k]);
}
printf("\n");
dfs(i+1);
if(!flag)
{
sum-=a[i];
visit[i]=0;
}
}
}
int main()
{
int i;
while(scanf("%d%d",&n,&k)!=EOF)
{
for(i=0; i<n; i++)
scanf("%d",&a[i]);
memset(visit,0,sizeof(visit));
flag=false;
dfs(0);
if(!flag)
printf("NO\n");
}
return 0;
}
提交 WA !!!
还有个问题 第二次输入的值不正确
#include <cstdio>
#include <cstring>
using namespace std;
int a[30],visit[30],n,k,sum;
bool flag;
void dfs(int pos)
{
if(sum==k)
{
flag=true;
printf("YES\n");
for(int j=0; j<n; j++)
{
if(visit[j]==1)
{
printf("%d ",a[j]);
}
}
printf("\n");
return ;
}
if(sum>k)
{
return;
}
for(int i=pos; i<n; i++)
{
sum+=a[i];
visit[i]=1;
/*printf("%d %d %d\n",pos,i,sum);
for(int k=0; k<n; k++)
{
printf("%d ",visit[k]);
}
printf("\n");*/
dfs(i+1);
sum-=a[i];
visit[i]=0;
}
}
int main()
{
int i;
while(scanf("%d%d",&n,&k)!=EOF)
{
sum=0;
for(i=0; i<n; i++)
scanf("%d",&a[i]);
memset(visit,0,sizeof(visit));
flag=false;
dfs(0);
if(!flag)
printf("NO\n");
}
return 0;
}
AC代码 发现不同没
没错
if(!flag)
{
sum-=a[i];
visit[i]=0;
}
这里改动一下~~~本来是为了防止回去时候把标记清除了的
但是我已经提前输出结果了 标记无所谓了 没去掉
重点 他影响了第二次的运行 这个 为什么影响 大家下去自己思考
真的第一次写这么长的博客 记录我写一道题的经理
真的卡在这道题上太多时间~~~~
不过收获很大 时间用的还是很值得的
总结;
做题不要看网上代码
以前不会直接百度代码 不知道每一个代码都有这么多自己想不到的门道
先自己 写一个代码 (自己认为正确的)
即使出不来结果也无所谓了 我们慢慢改
做一道会一道