ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-05-22 11:00:12  阅读:256  来源: 互联网

标签:map 1805 NOI int 纸片 start total minDiff string


题目

原站链接

描述

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

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;
	}
}

标签:map,1805,NOI,int,纸片,start,total,minDiff,string
来源: https://blog.csdn.net/weixin_44841943/article/details/117152013

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有