洛谷(P1106)删数问题(dfs+剪枝)

题目描述
键盘输入一个高精度的正整数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,这里我搞不懂,希望有高手给我解惑。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会当凌绝顶丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值