[NOIP1998 提高组] 拼数
题目描述
设有 n n n 个正整数 a 1 … a n a_1 \dots a_n a1…an,将它们联接成一排,相邻数字首尾相接,组成一个最大的整数。
输入格式
第一行有一个整数,表示数字个数 n n n。
第二行有 n n n 个整数,表示给出的 n n n 个整数 a i a_i ai。
输出格式
一个正整数,表示最大的整数
样例 #1
样例输入 #1
3
13 312 343
样例输出 #1
34331213
样例 #2
样例输入 #2
4
7 13 4 246
样例输出 #2
7424613
提示
对于全部的测试点,保证 1 ≤ n ≤ 20 1 \leq n \leq 20 1≤n≤20, 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1≤ai≤109。
NOIP1998 提高组 第二题
题解
最朴素的方法,直接搜索,看数据规模如果剪枝好一点说不准能过
代码如下:
#include<iostream>
#include<cstring>
using namespace std;
int n;
bool book[21];
string s[21],f,ans;
void dfs(int step)
{
if(f<ans.substr(0,f.length())) return; //利用string比大小本质是字典序的比较,一旦发现小了就剪枝
if(step>n) //只有一直不剪枝的才能用来更新
{
ans=f;
return;
}
for(int i=1;i<=n;i++)
{
if(!book[i]) //没用过的数就用来扩展
{
book[i]=1;
f+=s[i];
dfs(step+1);
f.erase(f.length()-s[i].length());
book[i]=0;
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>s[i];
ans+=s[i];
}
dfs(1);
cout<<ans;
return 0;
}
然而TLE了好多,不过中间写剪枝的时候,受到了string比较字典序的启发,且刚好这个搜索的代码用来当std对拍用
问题可以变为对这些数按字符串进行某种排序,使得最后连起来刚好为答案,排序的法则是:
1.如果两个字符串在公共长度部分直接比出字典序大小,那么就依此确定先后关系
2.如果未在公共长度部分比出字典序大小,那么就比较短字符串的第一个字符和长字符串截断处后的第一个字符,依此确定先后关系
看数据规模,手写一个冒泡排序应该也能过,不过为了省时省力,直接用sort就好了,手写一个cmp:
#include<iostream>
#include<algorithm>
using namespace std;
int n;
string a[21];
bool cmp(string a,string b)
{
if(a==b) return false;
bool swap_flag=0;
if(a.length()>b.length()) {
swap(a,b);
swap_flag=1;
}
int len=a.length();bool ans;
if(a==b.substr(0,len)) ans=b[len]<a[0];
else ans=a>b;
return swap_flag? !ans:ans;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
cout<<a[i];
return 0;
}
可以看出来写的很复杂,问题核心在哪里?
在于是两个不同长度的字符串比较大小(且不能完全按string默认的比较大小来),那么吸取经验,如果两个字符串是相同长度的,那么就可以按string默认的字典序来了,无需手写那么多细节
比较大小的根本目的是为了判断谁放在前面,谁放在后面,那么我们模拟一下谁在前谁在后就行了,即判断a+b
和b+a
的字典序大小,这样长度是一致的,让string对哪个方案好做出决策
代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
int n;
string a[21];
bool cmp(string a,string b)
{
return a+b>b+a;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
cout<<a[i];
return 0;
}