hdu6351 Beautiful Now(穷竭搜索之next_permutation)

题目

给你一个十进制数n(n<=1e9),一个数k

自己可以和自己交换,

也可以理解成可以不用次数

问最多k次交换后,

这个数n最小最大各是多少

思路来源

https://www.cnblogs.com/xiuwenli/p/9432817.html

题解

思路来源的题主代码写的好评,赞一个

时间卡的非常紧 2500ms的题2300ms过了

毕竟全排列10!*100跑满的话要3e8多

考虑暴力枚举每个数,

如果没有前导0且符合最小交换小于等于k次

就更新一下最大最小值

 

怎么统计最小交换次数呢,

如果单纯把每一位拆开跑全排列,

可能会有重复的数,这样统计的时候会有麻烦

不如我们跑第i位的全排列,即下标的全排列,就不会重复

 

然后找到每个下标的环

比如说原来是01234(pos数组)

某个排列中是10342,

这样10构成了一个环,342构成了一个环

不难发现,每个环两两交换使之还原成原来的样子,

所需的次数是环里的数个数-1,

因为除了最后一次,

之前的每一次只能使一个数复位

心得

以后题死抠抠不对的时候,及时看题解,或许真的是自己想不到的解法

好像暑假的时候做过这个题,当时没补,在6个月后又惨遭爆锤

之前想过一个把右起最大和左起最小换,然后向低位扫搞最大值

第一次特判前缀0把右起最小和左起最大换,再向后类似的不特判0搞最小值的做法

现在发现不行,毕竟右起和左起换,是可能会破坏原本的最小环结构的

这样操作可能使得把两个小环合成一个大环,从而交换更多次

具体反例也举不出来,就这样叭

另外那个i=pos[i]的找环操作真是惊艳吖

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=11;
int t,k;
char s[maxn];
int num[maxn];
int pos[maxn];
bool vis[maxn];
ll ans1,ans2,n;
//01234 原pos数组
//10342 某个全排列
//则i=pos[i]会找到环 
//每个cnt的节点的环需要cnt-1次交换才能归位 
bool check(int len)
{
	int res=0,cnt=0;;
	memset(vis,0,sizeof(vis));
	for(int i=0;i<len;++i)
	{
		if(vis[i])continue;
		while(!vis[i])
		{
			cnt++;
			vis[i]=1;
			i=pos[i];
		}
		res+=cnt-1;
		if(res>k)return 0;
		cnt=0; 
	}
	return 1;
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%s%d",s,&k);
		int len=strlen(s);
		n=0;
		for(int i=0;i<len;++i)
		{
			num[i]=s[i]-'0';
			n=n*10+num[i];
			pos[i]=i;
		}
		ans1=ans2=n;
		//注意全排列对下标全排列,不要对num[]全排列
		//num中有相同的元素不好处理 下标一定不会重复
		do
		{
			ll tmp=0;
			for(int i=0;i<len;++i)
			tmp=tmp*10+num[pos[i]];
			if(num[pos[0]]&&check(len))//无前导零且可行 
			{
			 if(tmp<ans1)ans1=tmp;
			 if(tmp>ans2)ans2=tmp;
		    }
		}while(next_permutation(pos,pos+len));
		printf("%lld %lld\n",ans1,ans2); 
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Code92007

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

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

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

打赏作者

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

抵扣说明:

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

余额充值