题目传送门
P11229 [CSP-J 2024] 小木棍
题解思路:
1、首先想到的是搜索,全排列填盒子的思想,小木棍数够组成某个数,把某个数放到盒子里,这里就是累加到sum上,还做了个剪枝,中间如果已经大于结果res,不再进行搜索,得了20分。仔细看了一下数据,n最大10的5次方,很显然能够组成的数非常大,大的long long都存不下。
20分搜索代码如下,尽管不对,但还是挺标准的全排列填盒子搜索思想的。
#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int t, n;
int nums[10]={6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
int box[N], flag;
long long res;
void dfs(int idx, int sum, int n){
if(sum > res) return;
if(n == 0){
flag = 1;
res = sum;
}
for(int i=0; i<10; i++){
if(n >= nums[i]){
if(i == 0 && idx == 0) continue;
box[idx] = i;
dfs(idx+1, sum*10+i, n-nums[i]);
}
}
}
int main() {
cin>>t;
while(t--){
cin>>n;
flag = 0;
res = 2147483647;
dfs(0, 0, n);
if(flag==0) cout<<-1<<endl;
else cout<<res<<endl;
}
return 0;
}
2、考虑到能够组成的数非常大,大的long long都存不下,long long存不下的数用什么,这不就马上想到了大整数。思路2:使用字符串存储整数。这次得了10分,其他全部超时。看来思路1多得的10分是瞎猫碰上死耗子。超时算是从根本上否决了搜索的思路。
#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int t, n;
int nums[10]={6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
string res;
bool compare(string s1, string s2){
if(s1.size() > s2.size() || (s1.size() == s2.size() && s1>s2)) return true;
return false;
}
void dfs(bool first, string sum, int n){
if(res != "" && compare(sum, res)) return;
if(n == 0){
res = sum;
return;
}
for(int i=0; i<10; i++){
if(n >= nums[i]){
if(i == 0 && first) continue;
dfs(false, sum+char(i+'0'), n-nums[i]);
}
}
}
int main() {
cin>>t;
while(t--){
cin>>n;
res = "";
dfs(true, "", n);
if(res == "") cout<<-1<<endl;
else cout<<res<<endl;
}
return 0;
}
3、再次返回题目,题目对于数据范围的描述,提点了特殊性质A和特殊性质B,那在这两种情况下很可能存在某个规律,经过举例数据验证:当满足特殊性质A:n是7的倍数,全由8组成,一共 (n/7) 个8;当满足特殊性质B:n是7的倍数加1,开头为 10,剩下全是8,一共 (n/7-1) 个8。把两种特殊情况单独处理,分数飙升到了70分。
4、特殊情况下有规律,寻求非特殊情况下是否也有规律呢,打表(前边的搜索方案也不是毫无用处吗,这不就用上了吗),打表发现,除了个别数据,都是存在规律的,完全可以从前推到后,本题是多测试样例,为了避免重复计算,考虑空间换时间,把1~10^5都存下来,计算那个直接查表。
0分代码,倒是不超时了,全部样例超存储,空间换时间也有看实际情况啊。
#include<bits/stdc++.h>
using namespace std;
const int N = 100001;
int n, t;
string res[N]={"", "-1", "1","7","4","2","6","8","10","18","22","20","28","68","88","108","188","200"};
int main() {
for(int i=18; i<N; i++){
res[i] = res[i-7]+"8";
}
cin>>t;
while(t--){
cin>>n;
cout<<res[n]<<endl;
}
return 0;
}
5、观察打表可知,1~17存在部分数据不符合规律,使用固定数组存储。18及以后的数据可分解为若干个7 加 一个小于等于17的数字。最后组的数字即:一个小于等于17的数字可组成的数+若干个8(可分出7的个数)
满分代码横空出世
#include<bits/stdc++.h>
using namespace std;
int n, t;
string res[18]={"", "-1", "1","7","4","2","6","8","10","18","22","20","28","68","88","108","188","200"};
int main() {
cin>>t;
while(t--){
cin>>n;
if(n<18) cout<<res[n]<<endl;
else{
string tmp="";
while(n>=18){
tmp += "8";
n -= 7;
}
tmp = res[n]+tmp;
cout<<tmp<<endl;
}
}
return 0;
}