POJ 1416 && OpenJudge 1805 Shredding Company (DFS+剪枝)

1805:碎纸机
查看 提交 统计 提问
总时间限制: 1000ms 内存限制: 65536kB
描述
你现在负责设计一种新式的碎纸机。一般的碎纸机会把纸切成小片,变得难以阅读。而你设计的新式的碎纸机有以下的特点:

1.每次切割之前,先要给定碎纸机一个目标数,而且在每张被送入碎纸机的纸片上也需要包含一个数。
2.碎纸机切出的每个纸片上都包括一个数。
3.要求切出的每个纸片上的数的和要不大于目标数而且与目标数最接近。

举一个例子,如下图,假设目标数是50,输入纸片上的数是12346。碎纸机会把纸片切成4块,分别包含1,2,34和6。这样这些数的和是43 (= 1 + 2 + 34 + 6),这是所有的分割方式中,不超过50,而又最接近50的分割方式。又比如,分割成1,23,4和6是不正确的,因为这样的总和是34 (= 1 + 23 + 4 + 6),比刚才得到的结果43小。分割成12,34和6也是不正确的,因为这时的总和是52 (= 12 + 34 + 6),超过了50。

还有三个特别的规则:
1.如果目标数和输入纸片上的数相同,那么纸片不进行切割。
2.如果不论怎样切割,分割得到的纸片上数的和都大于目标数,那么打印机显示错误信息。
3.如果有多种不同的切割方式可以得到相同的最优结果。那么打印机显示拒绝服务信息。比如,如果目标数是15,输入纸片上的数是111,那么有两种不同的方式可以得到最优解,分别是切割成1和11或者切割成11和1,在这种情况下,打印机会显示拒绝服务信息。

为了设计这样的一个碎纸机,你需要先写一个简单的程序模拟这个打印机的工作。给定两个数,第一个是目标数,第二个是输入纸片上的数,你需要给出碎纸机对纸片的分割方式。
输入
输入包括多组数据,每一组包括一行。每行上包括两个正整数,分别表示目标数和输入纸片上的数。已知输入保证:两个数都不会以0开头,而且两个数至多都只包含6个数字。

输入的最后一行包括两个0,这行表示输入的结束。
输出
对每一组输入数据,输出相应的输出。有三种不同的输出结果:

sum part1 part2 …
rejected
error

第一种结果表示:
1.每一个partj是切割得到的纸片上的一个数。partj的顺序和输入纸片上原始数中数字出现的次序一致。
2.sum是切割得到的纸片上的数的和,也就是说:sum = part1 + part2 +…
第一种结果中相邻的两个数之间用一个空格隔开。

如果不论怎样切割,分割得到的纸片上数的和都大于目标数,那么打印“error”。
如果有多种不同的切割方式可以得到相同的最优结果,那么打印“rejected”。
样例输入
50 12346
376 144139
927438 927438
18 3312
9 3142
25 1299
111 33333
103 862150
6 1104
0 0
样例输出
43 1 2 34 6
283 144 139
927438 927438
18 3 3 12
error
21 1 2 9 9
rejected
103 86 2 15 0
rejected

思路:
1.如果纸片上每一位划分一个一个数的时候,这是和最小的情况,若Σ > 目标 那么就应返回error。
2.切分方法:先考虑每次切分一个点和多个点,然后在讨论其他情况的也是这样 然后在考虑切分成多个点和多个点 以此类推
图片来自@http://blog.csdn.net/lyy289065406
这里写图片描述
3.划分的时候前面的部分并入∑,剩下的部分继续按上一步划分 知道划分至数串长度为零
4.我们读数的时候先将数串转化为字符
5.如果在加的过程中就超过了目标 就结束搜索 重新开始
6.先处理和 判断是否会error 搜完之后在判断reject
7.若当前最优解与之前的相同 sum++ 否则更新最优解 sum = 1;

#include<bits/stdc++.h>
using namespace std;
int ans,m,n,vis[1000005],step[10],now[10],num;  
//最大答案 纸片上的数 目标 答案出现的次数 划分成的最优解 当前方案 划分数的个数 
char s[10];  
//把数划分成字符 
void dfs(int pos,int sum,int cnt)  
//划分后串的位置 当前和  次数 
{  
    if(pos >= m)
    {  
        vis[sum] ++;  
        if(sum > ans)
        {  
            ans = sum;  //记录最佳切割的和  
            num = cnt;  
            for(int i = 0;i < cnt;i ++)  
            step[i] = now[i];  //记录当前最佳方案 
        }  
        return ;  
    }  
    int t = 0;  
    for(int i = pos;i < m;i ++)//每次递归结束再去切 
    {  
        t = t * 10 + s[i] - '0';//得到数字字符串s前i位字符(数字)组成的int值 
        if(sum + t > n)  return ;  
        now[cnt] = t;  
        dfs(i + 1,sum + t,cnt + 1);  
    }  
}  
int main()  
{  
    int sum;  
    while(scanf("%d%s",&n,s)!=EOF)
    {  
        m = strlen(s);  
        if(n == 0 && s[0] == '0' && m == 1)  break;
        sum = 0;  
        for(int i = 0;i < m;i ++)  
            sum += s[i] - '0';  
        if(sum > n)//若最小的和都大于targe,则肯定不可能切割出不超过targe的和 
        {    
            printf("error\n");  
            continue;  
        }  
        memset(vis,0,sizeof(vis));  
        num = ans = 0;  
        dfs(0,0,0);  
        if(vis[ans] > 1) printf("rejected\n");  
        else
        {  
            printf("%d",ans);  
            for(int i = 0;i < num;i ++)  
                printf(" %d",step[i]);  
            printf("\n");  
        }  
    }  
    return 0;  
}  
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值