POJ.1416 Shredding Company (DFS)

POJ.1416 Shredding Company [从零开始DFS(8)]

点我挑战题目

从零开始DFS
HDOJ.1342 Lotto [从零开始DFS(0)] — DFS思想与框架/双重DFS
HDOJ.1010 Tempter of the Bone [从零开始DFS(1)] —DFS四向搜索/奇偶剪枝
HDOJ(HDU).1015 Safecracker [从零开始DFS(2)] —DFS四向搜索变种
HDOJ(HDU).1016 Prime Ring Problem (DFS) [从零开始DFS(3)] —小结:做DFS题目的关注点
HDOJ(HDU).1035 Robot Motion [从零开始DFS(4)]—DFS题目练习
HDOJ(HDU).1241 Oil Deposits(DFS) [从零开始DFS(5)] —DFS八向搜索/双重for循环遍历
HDOJ(HDU).1258 Sum It Up (DFS) [从零开始DFS(6)] —DFS双重搜索/去重技巧
HDOJ(HDU).1045 Fire Net [从零开始DFS(7)]—DFS练习/check函数的思想
POJ.1416 Shredding Company [从零开始DFS(8)]—DFS练习/sum函数的设计

题意分析

给出一个目标数字n和一串数字序列s,要求切割序列s为若干数字段s1,s2,s3……sn,最后求出最大的 不超过目标数字n的 序列s切割方式,和这些字段的和。若对于最大的数字有多种切割方式,则输出rejected;若没有这样的切割方式,则输出error。

直接对给出的数字串操作有些不方便,我们不妨想想挡板法。对于给出的序列s(若其有x位),则有x-1个空挡。对于每一个空挡,我们有选/不选两种选择,这又回到了讨论过的那种选/不选的模型,可以用dfs暴力枚举来解决此类问题。

题目要求输出这样的分割方式和最大的和,则在dfs函数中一定有更新最大和的部分,并且还要保存这样的分割方式。递归边界就是,如果分割完了,那么就计算一下当前的和,如果和比目前的最大和大并且比目标值小,就更新,否则就舍弃。

分析到这里其实就差不多了,我在做此题时比较伤脑筋的是如何计算这样这样字段的和,还有如何保存起来。想到的解决办法是:
1.用一个数组gap来表示间隔。对于数字序列num[1]num[2]……num[n](num[i]表示的是一位数字),gap[i]表示num[i]和num[i+1]中是否有间隔。若有则为1,否则为0;
2.为便于计算字段和,规定gap[0] = gap[n] = 1(n为给出的字符序列的长度,如12345的长度为5);
3.用save数组表示当前最优的分割方式。

上代码。

代码总览

/*
    Title:POJ.1416
    Author:pengwill
    Date:2017-2-14
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
char nu[100];
int num[100],gap[100],save[100];
int ret,len,test,time= 0;
long long tar;
bool mult = false, judge = false;
void init()
{
    memset(gap,0,sizeof(gap));
    len = strlen(nu);
    for(int i = 0,j = 1; i<len;++i,++j) num[j] = nu[i]-'0';
    judge = mult  = false;
    ret = 0;gap[0] = gap[len] = 1; test = 0;time = 0;
}
int sum()
{
    int base = 1; int ans = 0; int t = 0;
    for(int i = len ; i>=0; --i){
        if(gap[i] == 0){
            base*=10;
            t += num[i] * base;
        }else if(gap[i] == 1){
            ans+=t; base = 1;
            t = num[i] * base;
        }
    }
    return ans;
}
void cpy()
{
    for(int i = 0; i<=len ;++i) save[i] = gap[i];
}
// gap[i] (1<=gap<=len)
// gap[i] = 0 -> not chose;
// gap[i] = 1 -> chose;
void output()
{
    for(int i = 1;i<=len;++i){
        printf("%d",num[i]);
        if(i!=len){
            if(save[i] == 1) printf(" ");
        }
    }
    printf("
");
}
void dfs(int depth,int tag)
{
    if(len==depth){
        int t = sum();
        if(t>ret && t<=tar){
            judge = true;
            time = 1;
            cpy();
            ret = t;
        }else if(t==ret && t<=tar){
            time++;
        }
        return;
    }
    gap[depth] = tag;
    if(depth == len-1){
        dfs(depth+1,1);//chose gap
    }else{
        dfs(depth+1,1);//chose gap
        dfs(depth+1,0);//don't chose gap
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%lld %s",&tar,nu) && tar){
        init();
        dfs(0,1);
        if(judge){
            if(time>1) printf("rejected
");
            else {printf("%d ",ret);output();}
        }else printf("error
");
    }
    return 0;
}

比较难设计的就是sum函数。

int sum()
{
    int base = 1; int ans = 0; int t = 0;
    for(int i = len ; i>=0; --i){
        if(gap[i] == 0){
            base*=10;
            t += num[i] * base;
        }else if(gap[i] == 1){
            ans+=t; base = 1;
            t = num[i] * base;
        }
    }
    return ans;
}

sum 函数是从右向左计算的,大致思路为:
1.如果gap[i] = 1, 则将t累加到ans上,重置base和t;
2.如果gap[i] = 0,则base自乘10,然后和num[i]相乘累加到t上。
自由向左,相当于模拟的从低位到高位,算起来比较自然。cpy函数完成对最优解的拷贝。inti函数预处理,将读入的字符串转换成数字。然后就是注意对多解的判断。

桃李春风一杯酒,江湖夜雨十年灯。

原文地址:https://www.cnblogs.com/pengwill/p/7367166.html