• 全排列和几道例题


    以前用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决赛]平方十位数

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

  • 相关阅读:
    hdoj 1002大数加法
    nuxt踩坑
    vue 打包上线后 css3渐变属性丢失的问题解决方案
    linux下crontab不能运行问题
    [转]谈谈数据库的ACID
    web集群时session共享
    redis缓存队列+MySQL +php任务脚本定时批量入库
    Yii2 加载css、js 载静态资源
    PHP实现四种基本排序算法
    phpstorm快捷键
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/12342359.html
Copyright © 2020-2023  润新知