题目描述
键盘输入一个高精度的正整数N(不超过250位) ,去掉其中任意k个数字后剩下的数字按原左右次序将组成一个新的正整数。编程对给定的N和k,寻找一种方案使得剩下的数字组成的新数最小。
输入格式:
输入两行正整数。
第一行输入一个高精度的正整数 nn。
第二行输入一个正整数 kk,表示需要删除的数字个数。
输出格式:
输出一个整数,最后剩下的最小数。
输入
175438
4
输出
13
这个题目我看网上挺多贪心解法的,我略微一看,字有点多,所以说就有一点想偷懒的感觉了,咱就说会不会这个题目还有别的比较通俗易懂的解法,你别说还真有,在这里给大家介绍一手宇宙无敌最强之dfs法,其实简单来说就是暴力破解(毕竟数据才250嘛,能暴力还要啥自行车)。思路其实很简单,你看要我删掉几个数然后找最小嘛,欧克,我把所有情况都列出来,然后找最小不就欧克了嘛,so easy。
#include"bits/stdc++.h"
using namespace std;
const int N=300;
int vis[N];//用以标记当前节点是否走过
int k;
string min_s,s,now_s;
void dfs(int n,int index)//n代表当前已选数,index代表从何处开始选
{
if(n==k)//如果当前已经有k个数(注意k被我们弄成剩余个数了,不要傻傻认为还是要删掉的数)
{
if(now_s<min_s)
min_s=now_s;//更新min_s
return;
}
for(int i=index;i<s.length();i++)
{
if(vis[i]==0)
{
string front_s=now_s;//注意要记录前面的now_s因为dfs过后要回溯
now_s+=s[i];
vis[i]=1;
dfs(n+1,i+1);//当前点可选则选入然后进入下一个dfs
now_s=front_s;
vis[i]=0;//回溯
}
}
}
int main()
{
cin>>s;
cin>>k;
min_s+=('9'+1);//初始化max_s比9就行毕竟是字符串比较
k=s.length()-k;//要删掉k个数等于要留s.length()-k个
dfs(0,0);
int index=0;
while(min_s[index]=='0') index++;//去除前导0
for(int i=index;i<min_s.length();i++)
cout<<min_s[i];
if(index==min_s.length())//如果有特殊情况像:0000的情况
cout<<0;
return 0;
}
就这么简单嘛,暴力破解随便拿捏。然后当你美滋滋的提交时,结果是熟悉的老朋友TLE。究其原因嘛,还是暴力破解的解题效率不行,它可以解决大部分题目,却不能让它们都在规定时间完成。这就可以让我放弃暴力吗这是不可能滴,我们还有后手,给它修饰一下。如果说当前now_s已经比min_s大了,我们还有必要往它后面加数然后比较吗?显然是不用的,因此我们如果发现已经没有必要比的时候就可以跳出循环了,然后就可以得到以下代码了。
#include"bits/stdc++.h"
using namespace std;
const int N=300;
int vis[N];//用以标记当前节点是否走过
int k;
string min_s,s,now_s;
void dfs(int n,int index)//n代表当前已选数,index代表从何处开始选
{
if(n==k)//如果当前已经有k个数(注意k被我们弄成剩余个数了,不要傻傻认为还是要删掉的数)
{
if(now_s<min_s)
min_s=now_s;//更新min_s
return;
}
for(int i=index;i<s.length();i++)
{
if(now_s>min_s)
break;//剪枝
if(vis[i]==0)
{
string front_s=now_s;//注意要记录前面的now_s因为dfs过后要回溯
now_s+=s[i];
vis[i]=1;
dfs(n+1,i+1);//当前点可选则选入然后进入下一个dfs
now_s=front_s;
vis[i]=0;//回溯
}
}
}
int main()
{
cin>>s;
cin>>k;
min_s+=('9'+1);//初始化max_s比9大1就行毕竟是字符串比较
k=s.length()-k;//要删掉k个数等于要留s.length()-k个
dfs(0,0);
int index=0;
while(min_s[index]=='0') index++;//去除前导0
for(int i=index;i<min_s.length();i++)
cout<<min_s[i];
if(index==min_s.length())//如果有特殊情况像:0000的情况
cout<<0;
return 0;
}
然后就可以成功ac了,这里我有一个问题,front_s如果放在全局变量就不能过全部测试点,但放在循环string front_s;就可以ac,这里我搞不懂,希望有高手给我解惑。