nyist-部分和问题

部分和问题

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 2
描述
给定整数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
来源
经典题目

这道题是水题?

我咋觉得一点也不水~~~~~

可能是我太水,我自己写的代码  发现了很多问题,结果都出现不了 下面我一点一点分析

#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;
        }
这里改动一下~~~本来是为了防止回去时候把标记清除了的  

但是我已经提前输出结果了  标记无所谓了    没去掉

重点  他影响了第二次的运行  这个  为什么影响  大家下去自己思考

真的第一次写这么长的博客 记录我写一道题的经理

真的卡在这道题上太多时间~~~~

不过收获很大  时间用的还是很值得的 


总结;

做题不要看网上代码 

以前不会直接百度代码  不知道每一个代码都有这么多自己想不到的门道

先自己 写一个代码  (自己认为正确的)

即使出不来结果也无所谓了  我们慢慢改  

做一道会一道  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值