题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1425
题意很简单,就是给出n,m求n个数下逆序个数为m的最小排列,求出最小排列。
我用的暴力+贪心的方法。 用m与当前个数(也就是剩下需要写出的个数)的最大逆序个数进行比较,容易知道当前逆序最大的个数为s=(n)*(n-1)/2;
如果s==m直接逆序打印没用过的数。如果m<s,那么继续用m与s1=(n-1)(n-2)/2,(n-1个最大逆序个数比较),然后找出没用过的(m-s1+1)个数,即为当前要打印的个数,
如果 m>=s,直接找一个最小没用过的数打印。
一开始没用long long还有各种细节不注意错了好几次,代码写得也很烂,网上好像有其他更简单的方法。
下面是AC代码:
#include<iostream>
#include<cstdio>
using namespace std;
int mark[50005];
int main(){
int i,j;
long long n,m,t;
while(cin>>n>>m){
// printf("%I64d\n",m);
if(n==-1&&m==-1)
break;
long long s=n*(n-1)/2;
// cout<<s<<endl;
// printf("%I64d\n",s);
// return 0;
int flag=1;
t=n;
for(i=1;i<=n;i++)
mark[i]=0;
if(m==0){
cout<<1;
for(i=2;i<=n;i++)
cout<<" "<<i;
cout<<endl;
}
else{
while(m){
if(m==s){
for(i=n;i>=1;i--){
if(mark[i]==0){
if(flag){
flag=0;
cout<<i;
mark[i]=1;
}
else{
cout<<" "<<i;
mark[i]=1;
}
}
}
break;
}
else if(m<s){
long long nexts=(t-1)*(t-2)/2;
if(m>nexts){
long long key=m-nexts;
int count=0,k;
for(i=1;i<=n;i++){
if(count==key+1) break;
if(mark[i]==0){
count++;
k=i;
}
}
if(flag){
cout<<k;
flag=0;
}
else{
cout<<" "<<k;
}
mark[k]=1;
m=nexts;
}
else{
for(i=1;i<=n;i++)
if(mark[i]==0){
if(flag){
flag=0;
cout<<i;
mark[i]=1;
break;
}
else{
cout<<" "<<i;
mark[i]=1;
break;
}
}
}
}
t--;
s=t*(t-1)/2;
}
cout<<endl;
}
}
return 0;
}