全排列中的数学问题

142 篇文章 0 订阅
73 篇文章 0 订阅

题目: https://www.luogu.org/problemnew/show/P1338
题解:
本题要用数学知识来做,如果用next_permutation来做,会严重超时。
正解:
如何让逆序对数为m的序列字典序最小呢? 假设位置p是第一个非原始序列的位置,那这个点应该尽量靠右,才能使得字典序最小。而为了保证有m个逆序对,要求p后面的逆序对数尽量大。怎么才能尽量大呢?当然是降序排列。这样问题就转成找点p,同时在找到p时还需要知道m-后面所有逆序对数剩余的值,这个值要在点p身上修改。则我们现在可以直接输出序列。
1)p之前的部分按照顺序输出
2)输出p,如果后面逆序对不够,则需要修改p点再输出。
3)逆序输出p之后的部分,注意如果p之前改点了,需要多判断一下。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,m;
	cin>>n>>m;
	int p=n,c=0;//p代表第一个非原始序列的位置 
	for(int s=1;m>0;s++,p--)
	{//找p的位置 
		c=min(s,m);//m代表一共还需要凑出多少逆序对 c代表本次p左移可以凑出多少逆序对 
		m-=c;//s代表p左移以为最多增加的逆序对个数 
	}
	for(int i=1;i<p;i++){
		cout<<i<<' ';
	}//输出顺序     
	cout<<p+c<<' ';//输出第一个非原始序列位置上的数
//找到P的位置后,这个位置上的数每增加一逆序对就增加一 
	for(int i=n;i>=p;i--){
		if(i!=p+c){
			cout<<i<<' ';
		}
	}
	return 0;
}

在这里插入图片描述

超时代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,m;
	int a[50005];	
	cin>>n>>m;
    for (register int i=1;i<=n;i++)	a[i]=i;
    while(1)
    {
    	int sum=0;
    	for(register int i=1;i<=n-1;i++)
    	    for(register int j=i+1;j<=n;j++)
    	        if(a[i]>a[j]) sum++;
		if(sum==m)
		{
			for(register int i=1;i<=n;i++) cout<<a[i]<<' ';
			return 0;
			}	
    	next_permutation(a+1,a+1+n);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值