首页 > 试题广场 >

数列还原

[编程题]数列还原
  • 热度指数:14916 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
牛牛的作业薄上有一个长度为 n 的排列 A,这个排列包含了从1到n的n个数,但是因为一些原因,其中有一些位置(不超过 10 个)看不清了,但是牛牛记得这个数列顺序对的数量是 k,顺序对是指满足 i < j 且 A[i] < A[j] 的对数,请帮助牛牛计算出,符合这个要求的合法排列的数目。

输入描述:
每个输入包含一个测试用例。每个测试用例的第一行包含两个整数 n 和 k(1 <= n <= 100, 0 <= k <= 1000000000),接下来的 1 行,包含 n 个数字表示排列 A,其中等于0的项表示看不清的位置(不超过 10 个)。


输出描述:
输出一行表示合法的排列数目。
示例1

输入

5 5
4 0 0 2 0

输出

2
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Collections;
public class Main {
    /**
    *如果给我们一个序列(不含有任何未知的元素),我们可以通过两次循环获取有多少个顺序对。
    *但是如果插入一个未知元素,顺序对变化了多少呢?等于这个元素与前面元素的新增的顺序对,和后面比较的新增的顺序对。
    *如果插入多个呢?可以逐次插入,让每次只有一个位置插入元素,其他未知临时忽略就可以了。这样就能递归解决问题。
    *一个序列的总的顺序对,等于插入前的对数,加上逐次插入未知产生的对数。
    *总的合法排列就等于未知数排列的合法排列数。
    */
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] arrInput = br.readLine().trim().split(" ");
        int n = Integer.parseInt(arrInput[0]);
        int k = Integer.parseInt(arrInput[1]);
        int[] arrNum = new int[n];
        int knownedSeqPair = 0;
        ArrayList<Integer> listIndex = new ArrayList<Integer>();//记住模糊位置的索引
        ArrayList<Integer> listBlur = new ArrayList<Integer>();//记住模糊数字
        HashSet<Integer> setBook = new HashSet<Integer>();//找到模糊数字的工具
        arrInput = br.readLine().trim().split(" ");
        for (int i=0; i<arrInput.length; i++) {
            arrNum[i] = Integer.parseInt(arrInput[i]);
            if (arrNum[i] == 0) {//模糊的记住位置,用于擦除数据
                listIndex.add(i);
            } else {//不模糊的记住数据本身,用于找到模糊的数据
                setBook.add(arrNum[i]);
            }
        }
        for (int i=1; i<=n; i++) {//查找模糊的数字
            if (false == setBook.contains(i)) {
                listBlur.add(i);
            }
        }
        for (int i=0; i<arrNum.length; i++) {
            if (arrNum[i] != 0) {//不能是模糊数字
                for (int j=i+1; j<arrNum.length; j++) {
                    if (arrNum[j] != 0 && arrNum[i] < arrNum[j]) {//不能是模糊数字
                        knownedSeqPair++;
                    }
                }
            }
        }
        System.out.println(Main.getSeqNum(arrNum, listBlur, listIndex, knownedSeqPair, 0, k));
    }
    public static int getSeqNum(int[] arrNum, ArrayList<Integer> listBlur, ArrayList<Integer> listIndex, int knownedSeqPair, int n, int k) {
        int result = 0;
        if (n == listBlur.size()) {//子排列已经出现
            int sumSeqNum = knownedSeqPair;
            int i = 0;
            int j = 0;
            for (i=0; i<listBlur.size(); i++) {//计算新增的排列
                for (j=0; j<arrNum.length && arrNum[j] != 0; j++) {//前半部分
                    if (arrNum[j] < listBlur.get(i)) {
                        sumSeqNum++;
                    }
                }
                if (j < arrNum.length) {//插入,为下次会比较,判断是为了应对根本没有未知的情况
                    arrNum[j++] = listBlur.get(i);
                }
                for (; j<arrNum.length; j++) {//比较后面
                    if (arrNum[j] != 0 && arrNum[j] > listBlur.get(i)) {
                        sumSeqNum++;
                    }
                }
            }
            for (i=0; i<listIndex.size(); i++) {//擦除模糊数字的排列,用于下次
                arrNum[listIndex.get(i)] = 0;
            }
            if (sumSeqNum == k) {
                result = 1;
            }
        } else {
            for (int i=n; i<listBlur.size(); i++) {//其实等于n是因为不交换(自交换)也是一种情况。
                Collections.swap(listBlur, n, i);
                result += Main.getSeqNum(arrNum, listBlur, listIndex, knownedSeqPair, n+1, k);
                Collections.swap(listBlur, n, i);
            }
        }
        return result;
    }
}

发表于 2018-11-03 14:55:12 回复(0)
  1. 计算缺失的数字及其位置
  2. 对缺失的数字进行全排列,然后填充到对应位置
  3. 计算填充之后的数组的k值

     // 计算合法数量
    private static int legalCount(int[] nums, int k){
         boolean[] used = new boolean[nums.length+1];
         List<Integer> leftNum = new ArrayList<>(),
                 leftIndex = new ArrayList<>();
         for(int i=0;i<nums.length;i++){
             if(nums[i]!=0) {
                 used[nums[i]] = true;
             }else{
                 leftIndex.add(i);
             }
         }
    
         for(int i=1;i<used.length;i++){
             if(!used[i])
                 leftNum.add(i);
         }
    
         List<List<Integer>> list = getOrder(leftNum);
         int count = 0;
         for(List<Integer> l : list){
             for(int i=0;i<leftIndex.size();i++){
                 nums[leftIndex.get(i)] = l.get(i);
             }
             if(calculateCount(nums, k))
                 count++;
         }
         return count;
     }
     // 排列组合
     private static List<List<Integer>> getOrder(List<Integer> nums){
         if(nums.size() == 1){
             List<Integer> temp = new ArrayList<>();
             temp.add(nums.get(0));
             List<List<Integer>> l = new ArrayList<>();
             l.add(temp);
             return l;
         }
         List<List<Integer>> result = new ArrayList<>();
         for(int i=0;i<nums.size();i++){
             int num = nums.get(i);
             nums.remove(i);
             List<List<Integer>> list = getOrder(nums);
             for(List<Integer> l : list){
                 l.add(num);
                 result.add(l);
             }
             nums.add(i, num);
         }
         return result;
     }
     // 计算顺序对是否满足要求
     private static boolean calculateCount(int[] nums, int aim){
         int count = 0;
         for(int i=0;i<nums.length;i++){
             for(int j=0;j<i;j++){
                 if(nums[j]!=0 && nums[i]> nums[j]){
                     count++;
                     if(count>aim)
                         return false;
                 }
    
             }
         }
         return count == aim;
     }
    
发表于 2018-07-26 11:03:01 回复(0)
import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (in.hasNext()) {
            int n = in.nextInt();
            int k = in.nextInt();
            int[] array = new int[n];
            boolean[] flag = new boolean[n];
            for (int i = 0; i < n; i++) {
                array[i] = in.nextInt();
                if (array[i] != 0) flag[array[i]-1] = true;
            }
            //找出缺失数字
            ArrayList<Integer> list = new ArrayList<>();
            for (int i = 0; i < n; i++)
                if (!flag[i]) list.add(i+1);
            //得到缺失数字全排列
            ArrayList<ArrayList<Integer>> listAll = new ArrayList<>();
            getPermutation(listAll, list, 0);

            //原始数组的顺序对
            int orderPairCount = getOrderPairByArray(array);
            int res = 0;
            //依次求出产生的全排列和原始数组一起的顺序对
            for (ArrayList<Integer> arrayList: listAll) {
                int count = orderPairCount;
                int[] temp = Arrays.copyOf(array, array.length);
                count += getOrderPairByNew(temp, arrayList);
                if (count == k) res++;
            }
            System.out.println(res);
        }
    }

    private static int getOrderPairByNew(int[] array, ArrayList<Integer> list) {
        int count = 0;
        int j = 0;
        for (int i = 0; i < array.length; i++) {
            if (array[i] == 0) {
                array[i] = list.get(j++);
                for (int k = 0; k < i; k++)
                    if (array[k] < array[i]) count++;
                for (int k = i+1; k < array.length; k++)
                    if (array[i] < array[k]) count++;
            }
        }
        return count;
    }

    private static int getOrderPairByArray(int[] array) {
        int count = 0;
        for (int i = 0; i <array.length; i++) {
            for (int j = i + 1; j < array.length && array[i] != 0; j++)
                if (array[j] != 0 && array[i] < array[j])
                    count++;
        }
        return count;
    }

    private static void getPermutation(ArrayList<ArrayList<Integer>> listAll,
                                       ArrayList<Integer> list, int start) {
        if (start == list.size() - 1)
            listAll.add(new ArrayList<>(list));
        else {
            for (int i = start; i < list.size(); i++) {
                Collections.swap(list, start, i);
                getPermutation(listAll, list, start + 1);
                Collections.swap(list, start, i);
            }
        }
    }
}  

发表于 2017-12-11 09:30:36 回复(0)
根据toraoh大神的代码写的java版。纯抄袭
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;


public class Main {     public static int calc(int[] arr, int k) {         int ans = 0;         for (int i = 0; i < k - 1; i++) {             for (int j = i + 1; j < k; j++) {                 if (arr[i] > 0 && arr[i] < arr[j]) {                     ans++;                 }             }         }         return ans;     }     //计算全排列     public static void calperm(List<ArrayList<Integer>> perm,List<Integer> missNumList, int n){         if(n == missNumList.size()){             perm.add(new ArrayList<Integer>(missNumList));         }else{             for(int i=n;i<missNumList.size();i++){                 Collections.swap(missNumList, i, n);                 calperm(perm,missNumList,n+1);                 Collections.swap(missNumList, i, n);             }         }     }     public static void main(String[] args) {         Scanner sc = new Scanner(System.in);         int n, k;         n = sc.nextInt();         k = sc.nextInt();         int[] li = new int[n];         boolean[] appear = new boolean[n];         int[] missIdx = new int[15];         int[] missNum = new int[15];         int missCnt = 0;         int[][] smaller = new int[n][n+1];         int[][] larger = new int[n][n+1];         // 保存输入,并记录缺失数据下标,及标记已出现的数字         for (int i = 0; i < n; i++) {             li[i] = sc.nextInt();             if (li[i] == 0) {                 missIdx[missCnt++] = i;             } else {                 appear[li[i] - 1] = true;             }         }         // 找出所有缺失的数字         missCnt = 0;         for (int i = 0; i < n; i++) {             if (!appear[i]) {                 missNum[missCnt++] = i + 1;             }         }         // 统计现有数字的顺序对         int given = calc(li, li.length);         // 统计缺失数字放在不同位置上,所产生的顺序对(small+larger)         for (int i = 0; i < missCnt; i++) {             int small = 0, large = 0;             for (int j = 0; j < n; j++) {                 if (li[j] == 0) {                     smaller[j][missNum[i]] = small;                 } else if (li[j] < missNum[i]) {                     small++;                 }             }             for (int j = n - 1; j >= 0; j--) {                 if (li[j] == 0) {                     larger[j][missNum[i]] = large;                 } else if (li[j] > missNum[i]) {                     large++;                 }             }         }         int ans = 0;         List<ArrayList<Integer>> missNumPerm = new ArrayList<ArrayList<Integer>>();         List<Integer> missNumList = new ArrayList<Integer>();         for(int i=0;i<missCnt;i++){             missNumList.add(missNum[i]);         }         calperm(missNumPerm,missNumList,0);         for(ArrayList<Integer> missN:missNumPerm){             Integer[] mNum = new Integer[missN.size()];             missN.toArray(mNum);             int inner = calc(Arrays.stream(mNum).mapToInt(Integer::valueOf).toArray(),missN.size());             for(int i=0;i<missCnt;i++){                 inner += smaller[missIdx[i]][mNum[i]];                 inner+=larger[missIdx[i]][mNum[i]];             }             if(inner+given==k) ++ans;         }         System.out.println(ans);     }
}

编辑于 2017-12-03 21:51:37 回复(0)
import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner in=new Scanner(System.in);
        int n=in.nextInt();
        int k=in.nextInt();
        int a[]=new int [n];
        int map[]=new int[n+1];
        for(int i=0;i<n;i++){
            a[i]=in.nextInt();
            map[a[i]]=1;
        }
        ArrayList<Integer>tag=new ArrayList<Integer>();
        for(int i=1;i<=n;i++){
            if(map[i]==0)
                tag.add(i);
        }
        ArrayList<ArrayList<Integer>>pers=permutation(tag,0);

        int count=0;
        for(int i=0;i<n;i++){
            if(a[i]!=0){
                for(int j=i+1;j<n;j++){
                    if(a[j]!=0&&a[i]<a[j])
                        count++;
                }
            }
        }
        int Res=0;
        for(ArrayList<Integer>temp:pers){
            int val=count;
            int []tempA=Arrays.copyOf(a,n);
            val+=calvalue(temp,tempA);
            if(val==k)
                Res++;
        }
        System.out.println(Res);
    }

   static ArrayList<ArrayList<Integer>>permus=new ArrayList<ArrayList<Integer>>();

    public static ArrayList<ArrayList<Integer>> permutation(ArrayList<Integer>a,int t){
        ArrayList<Integer> te=new ArrayList<Integer>();    
        int n=a.size();
        if(t>n)
            return null;
        if(t==n-1){
            permus.add(te);

        }

        for(int i=0;i<n;i++)
            te.add(a.get(i));


        for(int i=t;i<n;i++){
            int temp=a.get(t);
            a.set(t, a.get(i));
            a.set(i, temp);

            permutation(a,t+1);
            int temp1=a.get(t);
            a.set(t, a.get(i));
            a.set(i, temp1);

        } 
        return permus;
    }

    public static int calvalue(List<Integer>list,int [] A){
        int val=0;
        int j=0;
        for(int i=0;i<A.length;i++){
            if(A[i]==0){
                A[i]=list.get(j++);
                for(int k=0;k<i;k++)
                    if(A[k]!=0&&A[k]<A[i])
                        val++;
                for(int p=i+1;p<A.length;p++)
                    if(A[p]!=0&&A[p]>A[i])
                        val++;
            }

        }

        return val;
    }
}
编辑于 2017-09-03 22:26:35 回复(0)
链接:https://www.nowcoder.com/questionTerminal/b698e67a2f5b450a824527e82ed7495d
来源:牛客网

//方法是牛客网---hofighter提供的,个人只是加了一些注释,方便自己和大家理解
/**思路:首先将模糊的数字统计出来,并求出这些数字的全排列,然后对每个排列求顺序对
关键去求全排列,需要递归的求出所有排列。。。
ps:第一次做的时候没看清楚题,以为可以有重复数字,直接深搜计算了,结果。。。*/
importjava.util.ArrayList;
importjava.util.Arrays;
importjava.util.Collections;
importjava.util.List;
importjava.util.Scanner;
publicclassMain{
/**
* @param args
*/
publicstaticvoidmain(String[] args) {
// TODO Auto-generated method stub
Scanner sc =newScanner(System.in);
while(sc.hasNext()){
intRES =0;
intn = sc.nextInt();
intk = sc.nextInt();
int[] A =newint[n];
boolean[] flag =newboolean[n+1];//为什么是 n+1?因为n是从1开始的,必须有flag[n]
//flag标记哪些数字已经存在
for(inti=0;i<n;i++){
A[i] = sc.nextInt();
if(A[i] !=0){
flag[A[i]] =true;
}
}
//统计排列中不存在的数字
ArrayList<Integer> list =newArrayList<Integer>();
for(inti=1;i<=n;i++){
if(flag[i] ==false)
list.add(i);
}
//perm用来存模糊数字的全排列
List<ArrayList<Integer>> perm =newArrayList<ArrayList<Integer>>();
//计算perm
calperm(perm, list,0);
//统计已有的排列的顺序对
intcv =0;
for(inti=0;i<n;i++){
if(A[i]!=0){
for(intj=i+1;j<n;j++){
if(A[j] !=0&& A[i] < A[j])
cv++;
}
}
}
//计算每个模糊数字排列的顺序对,如果与k相等,则结果RES加一
for(ArrayList<Integer> tmp : perm){
intval = cv;
int[] tmpA = Arrays.copyOf(A, n);
val += calvalue(tmp, tmpA);
if(val == k)
RES++;
}
System.out.println(RES);
}
}
/**
* 计算排列的顺序对
* @param  list 模糊数列的某个排列
* @param A 最终的某个排列
*/
publicstaticintcalvalue(List<Integer> list,int[] A){
intval =0;
intj =0;
for(inti=0;i<A.length;i++){
if(A[i] ==0){
A[i] = list.get(j++);//每一一个为0的位置 安插一个list中的数字
for(intk =0;k<i;k++){
if(A[k]!=0&& A[k]<A[i])
val++;
}
for(intk=i+1;k<A.length;k++){
if(A[k]!=0&& A[k]>A[i])
val++;
}
}
}
returnval;//最后返回时因为必须报list中的所有数据安插在为0的位置上
}
/**
* 计算全排列
* @param perm
* @param list     排列中不存在的数字
* @param n 初始为0
*/
publicstaticvoidcalperm(List<ArrayList<Integer>> perm , ArrayList<Integer> list,intn){
if(n == list.size()){
perm.add(newArrayList<Integer>(list));
}else{
for(inti=n;i<list.size();i++){
Collections.swap(list, i, n);
calperm(perm, list, n+1);
Collections.swap(list, i, n);
}
}
}
}
这段代码看了一晚上了,最后全排列这里看不懂!!!!,这段代码有谁知道是什么意思吗?用了什么原理????看不出来递归在这里能起到什么作用???求大神帮忙啊!!!!
就是这段了:
链接:https://www.nowcoder.com/questionTerminal/b698e67a2f5b450a824527e82ed7495d?toCommentId=436352
来源:牛客网

  /**
     * 计算全排列
     * @param perm
     * @param list     排列中不存在的数字
     * @param n 初始为0
     */
    publicstaticvoidcalperm(List<ArrayList<Integer>> perm , ArrayList<Integer> list,intn){
        if(n == list.size()){
            perm.add(newArrayList<Integer>(list));
        }else{
            for(inti=n;i<list.size();i++){
                Collections.swap(list, i, n);
                calperm(perm, list, n+1);
                Collections.swap(list, i, n);
            }
        }
    }

用的是什么原理呢??
编辑于 2017-03-24 20:23:43 回复(0)