全排列和几道例题

以前用C++的next_permutation做题,现在用Java却找不到对应的全排列函数,不知怎么写,百度一搜,花里胡哨的一大堆,洋洋洒洒写了上百行代码,实在是看不下去,还是看题能找到简洁的代码。

裸题P1706:https://www.luogu.com.cn/problem/P1706

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define inf 0x3f3f3f3f
const double pi=3.1415926;
using namespace std;

int a[10];
bool vis[10];
int n;

void dfs(int x)
{
    if(x==n+1)
    {
        for(int i=1;i<=n;i++)
            printf("    %d",a[i]);
        printf("
");
        return;
    }
    for(int i=1;i<=n;i++)
    {
        if(vis[i]==false)
        {
            vis[i]=true;
            a[x]=i;
            dfs(x+1);
            vis[i]=false;
        }
    }
}



int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        memset(a,0,sizeof(a));
        memset(vis,false,sizeof(vis));
        dfs(1);
    }
    return 0;
}
P1706

NOJ1337:[蓝桥杯2017初赛]9数算式

观察如下的算式:9213 x 85674 = 789314562
左边的乘数和被乘数正好用到了1~9的所有数字,每个1次。
而乘积恰好也是用到了1~9的所有数字,并且每个1次。
请你借助计算机的强大计算能力,找出满足如上要求的9数算式一共有多少个?
注意:
1. 总数目包含题目给出的那个示例。
2. 乘数和被乘数交换后作为同一方案来看待。

思路:全排列,中间暴力插空*号,判断乘积,最后/2去掉对称。

import java.util.*;

public class Main{
    static int[] vis=new int[10];
    static int[] a=new int[10];
    static int ans=0;
    public static void main(String []args){    
        Scanner scan=new Scanner(System.in);
        dfs(1);
        System.out.println(ans/2);
        
    }
    
    public static void dfs(int x) {
        if(x==10) {
            for(int i=1;i<=8;i++) {    //第1个乘数有多少位
                int c=0,d=0;//计算两个乘数
                for(int j=1;j<=9;j++) {
                    if(j<=i)
                        c=c*10+a[j];
                    else 
                        d=d*10+a[j];
                }
                if(check(c*d)) 
                    ans++;
            }
            return;
        }
        for(int i=1;i<=9;i++){
            if(vis[i]==0)
            {
                vis[i]=1;
                a[x]=i;
                dfs(x+1);
                vis[i]=0;
            }
        }
    }
    
    public static boolean check(int a) {//将结果转化为字符串进行判断
        int[] flag=new int[10];
        int cnt=0;
        String s=String.valueOf(a);
        for(int i=0;i<s.length();i++) {
            char now=s.charAt(i);
            int x=(int)(now-'0');
            if(1<=x && x<=9 && flag[x]==0) {
                flag[x]=1;
                cnt++;
            }else 
                return false;
        }
        if(cnt==9)
            return true;
        else 
            return false;
    }
}
[蓝桥杯2017初赛]9数算式

NOJ1338:[蓝桥杯2017初赛]纸牌三角形

A,2,3,4,5,6,7,8,9 共9张纸牌排成一个正三角形(A按1计算)。要求每个边的和相等。 
下图就是一种排法这样的排法可能会有很多。 
如果考虑旋转、镜像后相同的算同一种,一共有多少种不同的排法呢? 

思路:看作是一行数字,1-4、4-7、7-9+1位置求和判断,最后/3去掉三角形的特性,再/2去掉镜像。

import java.util.*;

public class Main{
    static int[] vis=new int[10];
    static int[] a=new int[10];
    static int ans=0;
    public static void main(String []args){    
        Scanner scan=new Scanner(System.in);
        dfs(1);
        System.out.println(ans/6);
        
    }
    
    public static void dfs(int x) {
        if(x==10) {
            if(check())
                ans++;
            return;
        }
        for(int i=1;i<=9;i++){
            if(vis[i]==0)
            {
                vis[i]=1;
                a[x]=i;
                dfs(x+1);
                vis[i]=0;
            }
        }
    }
    
    public static boolean check() {//将结果转化为字符串进行判断
        int x=a[1]+a[2]+a[3]+a[4];
        int y=a[4]+a[5]+a[6]+a[7];
        int z=a[7]+a[8]+a[9]+a[1];
        if(x==y && y==z)
            return true;
        else 
            return false;
    }
}
[蓝桥杯2017初赛]纸牌三角形

P4163:[SCOI2007]排列

题意:t组数据,给个字符串和除数,求多少种全排列能被除数整除。t<=15,字符串长度n<=10,除数<=1000

这道题做了很久,不断优化查阅。

1.像上面的dfs一样找到一个全排列后数组遍历求值再判断,同时用HashMap去重,超时。(15*10!*log(10!)*10)

2.边dfs边求值,不必等到剪枝时遍历求值,超时。(15*10!*log(10!))

3.不用HashMap去重,HashMap要logn复杂度,对于有重复数字的情况用组合数求出重复多少次,结果再除去。例如333221这样的数每种排列会重复3!*2!*1!次。超时。(15*10!)

4.对我来说已经是极限优化了,还是过不了。去重的全排列或许能减少时间复杂度,但是不会写。Java代码改写成C++提交还是一样,开起O2优化多过了一组。其他数据还是超时。(15*10!)

5.正解是状压,可是我还不会状压,听着都唬人,强行用全排列做题。改用C++调用库函数next_permutation,全排列能自动去重。堂堂省选级别的题居然被库函数卡掉了。

import java.util.*;

public class Main{//P4163
    static int[] vis=new int[11];//是否访问过b数组的位置
    static int[] num=new int[11];//每个数字出现多少次
    static int[] a=new int[11];//dfs全排列数组
    static int[] b=new int[11];//存原序列,用来找各个位置上的数字
    static int[] fact=new int[11];//阶乘
    static int ans=0;
    static int d=0;
    static int len=0;
    static HashMap<Long, Integer> map=new HashMap<Long, Integer>();
    public static void main(String []args){    
        Scanner scan=new Scanner(System.in);
        
        //打印阶乘表
        fact[0]=1;
        for(int i=1;i<11;i++)
            fact[i]=i*fact[i-1];
        
        int t=scan.nextInt();
        while(t!=0){
            t--;
            //清空后重新输入
            ans=0;
            String s=scan.next();
            d=scan.nextInt();
            len=s.length();
            for(int i=0;i<len;i++) {
                int x=(int)(s.charAt(i)-'0');
                num[x]++;
                b[i+1]=x;
            }
            int q=1;//相同排列出现的次数
            for(int i=0;i<11;i++) {
                q=q*fact[ num[i] ];
                num[i]=0;//顺便清空
            }        
            dfs(1,0);
            System.out.println(ans/q);
        }
    }
    
    public static void dfs(int x,long now) {//十位数int溢出
        if(x==len+1) {
            if(now%d==0)
                ans++;
            return ;              
        }
        for(int i=1;i<=len;i++){
            if(vis[i]==0)//i不是数字,而是数组b各个位置
            {
                vis[i]=1;
                a[x]=b[i];
                dfs(x+1,now*10+b[i]);
                vis[i]=0;
            }
        }
    }
}
Java50分代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<ctime>
#define ll long long
#define inf 0x3f3f3f3f
const double pi=3.1415926;
using namespace std;

int t,len,d;
int a[11];
char s[15];
int ans=0;

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s%d",s,&d);
        ans=0;
        len=strlen(s);
        for(int i=0;i<len;i++)
        {
            int x=(int)(s[i]-'0');
            a[i+1]=x;
        }
        sort(a+1,a+len+1);///先排序
        do{
            ll sum=0;
            for(int i=1;i<=len;i++)
                sum=sum*10+a[i];

            if(sum%d==0)
                ans++;
        }while(next_permutation(a+1,a+len+1));

        printf("%d
",ans);

    }
    return 0;
}
C++100分代码

追:请教了大佬之后,修改一下代码用计数方法去重,在java50分代码的基础上化简后能够AC。

import java.util.*;

public class Main{//P4163
    static int[] num=new int[11];//每个数字出现多少次
    static int[] a=new int[11];//dfs全排列数组
    static int ans=0;
    static int d=0;
    static int len=0;
    static HashMap<Long, Integer> map=new HashMap<Long, Integer>();
    public static void main(String []args){    
        Scanner scan=new Scanner(System.in);
        
        int t=scan.nextInt();
        while(t!=0){
            t--;
            Arrays.fill(num, 0);
            //清空后重新输入
            ans=0;
            String s=scan.next();
            d=scan.nextInt();
            len=s.length();
            for(int i=0;i<len;i++) {
                int x=(int)(s.charAt(i)-'0');
                num[x]++;
            }
            dfs(1,0);
            System.out.println(ans);
        }
    }
    
    public static void dfs(int x,long now) {//十位数int溢出
        if(x==len+1) {
            if(now%d==0)
                ans++;
            return ;              
        }
        for(int i=0;i<=9;i++){
            if(num[i]>0) {//用计数方法排列
                num[i]--;
                dfs(x+1, now*10+i);
                num[i]++;
            }
        }
    }
}
Java超简洁代码

NOJ1341:[蓝桥杯2017决赛]平方十位数

题意:求0-9这10个数字组成的数中,最大的平方数。

import java.util.*;

public class Main{
    static int[] vis=new int[12];
    static int[] a=new int[12];
    static long ans=Long.MIN_VALUE;
    public static void main(String []args){    
        Scanner scan=new Scanner(System.in);
        dfs(0);
        System.out.println(ans);
    }
    
    public static void dfs(int x) {
        if(x==10 && a[0]!=0) {//首位不为0
            long sum=0;
            for(int i=0;i<=9;i++) {    //第1个乘数有多少位
                sum=sum*10+a[i];
            }
            long q=(long)Math.sqrt(sum);
            if(q*q==sum)
                ans=(long) Math.max(ans, sum);
            return;
        }
        for(int i=0;i<=9;i++){
            if(vis[i]==0)
            {
                vis[i]=1;
                a[x]=i;
                dfs(x+1);
                vis[i]=0;
            }
        }
    }
}
[蓝桥杯2017决赛]平方十位数

天上不会掉馅饼,努力奋斗才能梦想成真。

原文地址:https://www.cnblogs.com/shoulinniao/p/12342359.html