NOI 1805:碎纸机 DFS深度优先搜索/map记录答案/string转int

题目

原站链接

描述

你现在负责设计一种新式的碎纸机。一般的碎纸机会把纸切成小片,变得难以阅读。而你设计的新式的碎纸机有以下的特点:

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

分析

DFS深度优先搜索的思路

void dfs(int total, int start, string& s, int n,string& cur)

total是上一步求得的和,s是原字符串也就是纸条,start是现在要处理的字符串的开始位置在s中的下标,cur存储答案

void dfs(int total, int start, string& s, int n,string& cur){
	if(total > n){  //边界条件1:total>n,显然已经不符合要求
		return;
	}
	if(start == s.size()){ //边界条件2:start已经到原字符串的最后了,即原字符串已经全都枚举完毕 
		if(n-total < minDiff){
			minDiff = n-total;
			type = 1;
			mp[minDiff] = cur;
		}else if(n-total== minDiff){
			type = 2;
		}
		return;
	}
	//对于要处理的字符串,每次从start处截取长度为l的一段,剩余的字符串再DFS
	//l的大小从1一直到s.size()-start
	for(int l = 1;l<=s.size()-start;++l){
		int it = stoi(s.substr(start,l));  //string转int
		string st = cur;
		st += " ";
		st += s.substr(start,l);
		dfs(total+it, start+l, s, n,st);	
	}
}

AC代码

#include<bits/stdc++.h> 
using namespace std;
map<int, string> mp; //最小差值 → 切分结果
int type;  		//映射到不同的输出类型
int minDiff;  //求得的和与目标数的最小差值
void dfs(int total, int start, string& s, int n,string& cur){
	if(total > n){  //边界条件1:total>n,显然已经不符合要求
		return;
	}
	if(start == s.size()){ //边界条件2:start已经到原字符串的最后了,即原字符串已经全都枚举完毕 
		if(n-total < minDiff){
			minDiff = n-total;
			type = 1;  //当前最小值有一个解
			mp[minDiff] = cur;  //记录到map中
		}else if(n-total== minDiff){
			type = 2;  //当前最小值不止一个解
		}
		return;
	}
	//对于要处理的字符串,每次从start处截取长度为l的一段,剩余的字符串再DFS
	//l的大小从1一直到s.size()-start
	for(int l = 1;l<=s.size()-start;++l){
		int it = stoi(s.substr(start,l));  //string转int
		string st = cur;
		st += " ";
		st += s.substr(start,l);
		dfs(total+it, start+l, s, n,st);	
	}
}int main(){
	int n;
	string nums;
	while(cin>>n>>nums){
		if(n == 0 && nums == "0") break;
		//初始化操作
		type = 0;
		minDiff = 9999999;  //注意要够大,要大于999999
		mp.clear();
		string sol = "";
		
		dfs(0,0,nums,n,sol);
		
		if(type == 0)//如果不论怎样切割,分割得到的纸片上数的和都大于目标数
			cout<<"error"<<endl;
		else if(type > 1)//如果有多种不同的切割方式可以得到相同的最优结果
			cout<<"rejected"<<endl;
		else
			cout<<n-minDiff<<mp[minDiff]<<endl;
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值