洛谷题单 算法1-3 暴力枚举

1 First Step (ファーストステップ)

题目背景
知らないことばかりなにもかもが(どうしたらいいの?)

一切的一切 尽是充满了未知数(该如何是好)

それでも期待で足が軽いよ(ジャンプだ!)

但我仍因满怀期待而步伐轻盈(起跳吧!)

温度差なんていつか消しちゃえってね

冷若冰霜的态度 有朝一日将会消失得无影无踪

元気だよ元気をだしていくよ

拿出活力 打起精神向前迈进吧
洛谷题单 算法1-3 暴力枚举_第1张图片

我们Aqours,要第一次举办演唱会啦!

虽然学生会长看上去不怎么支持我们的样子,可是有了理事长的支持,我们还是被允许在校内的篮球场里歌唱!

歌曲也好好地准备过了,名字叫“最喜欢的话就没问题! (ダイスキだったらダイジョウブ!)“,大家一定会喜欢的吧!

演唱会一定会顺利进行的!

希望不要发生停电什么的事故哦……!

题目描述
可是……这个篮球场,好像很久没有使用过的样子啊……

里面堆满了学校的各种杂物呢……

我们Aqours的成员要怎么在里面列队站下呢?

我们浦之星女子学院的篮球场是一个R行C列的矩阵,其中堆满了各种学校的杂物 (用"#“表示),空地 (用”."表示) 好像并不多的样子呢……

我们Aqours现在已经一共有K个队员了,要歌唱舞蹈起来的话,我们得排成一条1*K的直线,一个接一个地站在篮球场的空地上呢 (横竖均可)。

我们想知道一共有多少种可行的站位方式呢。

Aqours的真正的粉丝的你,能帮我们算算吗?

输入格式
第一行三个整数 R, C, K。

接下来的R行C列,是浦之星女子学院篮球场。

输出格式
总共的站位方式数量。

输入输出样例
输入
5 5 2
.###.
##.#.
…#…
#…#.
#.###
输出
8
说明/提示
R C K 备注
1-2 <=10 <=10 <=min(R,C) 无
3-4 <=100 <=100 1 无
5-6 <=100 <=100 <=min(R,C) 没有障碍
7-10 <=100 <=100 <=min(R,C) 无
以下是彩蛋
在LoveLive!Sunshine!!动画第一季第三集中,Aqours队长高海千歌演唱“最喜欢的话就没问题!”到副歌前时,学校因为雷击停电。

思路:r和c的范围都不大,我们可以暴力去搜,每次向右搜k-1个点或者向下搜k-1个点,判断是否符合条件即可。需要注意的事,k=1的时候需要特判,这一点容易忽略。因为当k=1的时候,向右和向下搜会造成重复。



import java.util.Scanner;

public class Main {
    static char[][] ch;
    static boolean flag;
    static long r,c,k,ans;
    static int[][] next = {{0,1},{1,0}};//右 下
    public static void main(String[] args) {
      Scanner sc = new Scanner(System.in);
      r = sc.nextLong();
      c = sc.nextLong();
      k = sc.nextLong();
      ch = new char[(int)r][];
      for(int i=0;i<r;i++){
          ch[i]=sc.next().toCharArray();
      }
      for(int i=0;i<r;i++){
          for(int j=0;j<c;j++){
              if(ch[i][j]=='.'&&k!=1){
                  for(int t=0;t<2;t++){
                      int tx = i;
                      int ty = j;
                      flag=false;
                      for(int q=0;q<k-1;q++){
                          tx+=next[t][0];
                          ty+=next[t][1];
                          if(tx>=0&&tx<r&&ty>=0&&ty<c&&ch[tx][ty]=='.'){
                              continue;
                          }else{
                              flag=true;
                              break;
                          }
                      }
                      if(!flag){
                          ans++;
                      }
                  }
              }else if(ch[i][j]=='.'){
                  ans++;
              }
          }
      }
      System.out.println(ans);
    }
}

**

2 火星人

**
题目描述
人类终于登上了火星的土地并且见到了神秘的火星人。人类和火星人都无法理解对方的语言,但是我们的科学家发明了一种用数字交流的方法。这种交流方法是这样的,首先,火星人把一个非常大的数字告诉人类科学家,科学家破解这个数字的含义后,再把一个很小的数字加到这个大数上面,把结果告诉火星人,作为人类的回答。

火星人用一种非常简单的方式来表示数字――掰手指。火星人只有一只手,但这只手上有成千上万的手指,这些手指排成一列,分别编号为1,2,3…。火星人的任意两根手指都能随意交换位置,他们就是通过这方法计数的。

一个火星人用一个人类的手演示了如何用手指计数。如果把五根手指――拇指、食指、中指、无名指和小指分别编号为1,2,3,4和5,当它们按正常顺序排列时,形成了5位数12345,当你交换无名指和小指的位置时,会形成5位数12354,当你把五个手指的顺序完全颠倒时,会形成54321,在所有能够形成的120个5位数中,12345最小,它表示1;12354第二小,它表示2;54321最大,它表示120。下表展示了只有3根手指时能够形成的6个3位数和它们代表的数字:

三进制数

123
132
213
231
312
321

代表的数字

1
2
3
4
5
6

现在你有幸成为了第一个和火星人交流的地球人。一个火星人会让你看他的手指,科学家会告诉你要加上去的很小的数。你的任务是,把火星人用手指表示的数与科学家告诉你的数相加,并根据相加的结果改变火星人手指的排列顺序。输入数据保证这个结果不会超出火星人手指能表示的范围。

输入格式
共三行。
第一行一个正整数N,表示火星人手指的数目(1≤N≤10000)。
第二行是一个正整数M,表示要加上去的小整数(1≤M≤100)。
下一行是1到N这N个整数的一个排列,用空格隔开,表示火星人手指的排列顺序。

输出格式
N个整数,表示改变后的火星人手指的排列顺序。每两个相邻的数中间用一个空格分开,不能有多余的空格。

输入输出样例
输入
5
3
1 2 3 4 5
输出
1 2 4 5 3
说明/提示
对于30%的数据,N≤15;

对于60%的数据,N≤50;

对于全部的数据,N≤10000;

思路:这题真是日了狗了 ,菜鸡的我改了足足两个小时。。看了一眼洛谷的题解,45篇题解全是c++,没有一篇java的,好无奈。c++调stl只用十行代码就能ac,但是java不行。我刚开始手写了一个纯暴力的dfs,先找到火星人给的数是多少,然后在加上m,然后继续求全排列。结果内存超限。然后对代码进行了一点优化,此题我们不必在乎外星人给的数是多少,我们只要在火星人给的数的基础上求m次全排列即可。即,我们从外星人给出的全排列开始进行m次dfs,求出来的就是答案。

import java.util.Scanner;

public class Main {
    static boolean flag;
    static int n,m,count;
    static int[] ok,ans,book;
    public static void main(String[] args) {
        Scanner sc =new Scanner(System.in);
         n = sc.nextInt();
         m = sc.nextInt();
         ok = new int[n];
         ans = new int[n];
         book=new int[n+1];
         for(int i=0;i<n;i++){
             ok[i]=sc.nextInt();
         }
         dfs(0);
    }
    public static void dfs(int x){
        if(flag){
            return ;
        }
        if(x==n){
            count++;
            if(count>m){
                for(int i=0;i<n;i++){
                    if(i!=n-1){
                        System.out.print(ans[i]+" ");
                    }else{
                        System.out.print(ans[i]);
                    }
                }
                flag =true;
            }
            return ;
        }
        for(int i=1;i<=n;i++){
            if(count==0){
                i = ok[x];
            }
            if(book[i]==0){
                ans[x]=i;
                book[i]=1;
                dfs(x+1);
                book[i]=0;
            }
            if(flag){
                return ;
            }
        }
    }
}

3 统计方形(数据加强)

题目背景
1997年普及组第一题

题目描述
有一个n*m方格的棋盘,求其方格包含多少正方形、长方形

输入格式
n,m因为原来数据太弱,现规定m小于等于5000,n小于等于5000(原来是100,100)

输出格式
方格包含多少正方形、长方形

输入输出样例
输入
2 3
输出
8 10

思路:题意就是让我们在n*m的方格里找有多少个正方形,有多少长方形。这题对于数学菜鸡的我来说,真是不好做啊。。废活不多说,上图,先找规律。
洛谷题单 算法1-3 暴力枚举_第2张图片
看到这里,规律是不是呼之欲出啊,废话不多说,暴力就完了。



import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        long ans1 = 0;
        long ans2 = 0;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(i==j){
                    ans1+=(n-i)*(m-j);
                }else{
                    ans2+=(n-i)*(m-j);
                }
            }
        }
        System.out.print(ans1+" "+ans2);
    }
}

4 妖梦拼木棒

题目背景
妖梦斩了一地的木棒,现在她想要将木棒拼起来。

题目描述
有 n根木棒,现在从中选 4 根,想要组成一个正三角形,问有几种选法?
答案对 10^9+7 取模。

输入格式
第一行一个整数 n。

第二行 n 个整数,第 i 个整数 ai 代表第 i 根木棒的长度。

输出格式
一行一个整数代表答案。

输入输出样例
输入
4
1 1 2 2
输出
1
说明/提示
数据规模与约定
对于100% 的数据,保证 1≤n≤10 ^5,0≤ai≤5×10 ^3。

思路:假设四根木棒的长度为a,b,c,d。因为要组成正三角形,所以我们要保证四条木棒的长度符合a + b = c = d。即:四根木棒的长度为a,b,a+b。题目问我们一共有多少种情况,我们可以采用桶排序的方法储存每个长度出现的次数。对于答案,我们可以分两种情况来考虑。
1: a=b。则ans+=C(a,2)*C(a+b,2)。
2: a!=b。则ans+=C(a,1)*C(b,1)*C(a+b,2)。
问题转化为数学问题来求排列组合,需要注意的是,我们要随时取模。同时要注意a与b的大小问题,不然会重复,我们可以规定a>=b。

import java.util.Scanner;
public class Main {
    //a + b =a + b
    static long mod = 1000000007,n,max,ans;
    static int[] count = new int[5005];
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        n = sc.nextLong();
        for(int i=0;i<n;i++){
            int t = sc.nextInt();
            count[t]++;
            max = Math.max(max,t);
        }
        for(int a=1;a<=max;a++){
            for(int b=1;b<=a;b++){
                if(a+b<=max&&count[a+b]>=2){
                    if(a==b&&count[a]>=2){
                        ans=(ans+(count[a]*(count[a]-1)/2)*count[a+b]*(count[a+b]-1)/2)%mod;
                    }else if(a!=b&&count[a]!=0&&count[b]!=0){
                        ans=(ans+count[a]*count[b]*count[a+b]*(count[a+b]-1)/2)%mod;
                    }
                    ans%=mod;
                }
            }
        }
        System.out.println(ans);
    }
}

5 火柴棒等式

题目描述
给你n根火柴棍,你可以拼出多少个形如A+B=C的等式?等式中的A、B、C是用火柴棍拼出的整数(若该数非零,则最高位不能是0)。用火柴棍拼数字0−9的拼法如图所示:
在这里插入图片描述

注意:
加号与等号各自需要两根火柴棍
如果A≠B则A+B=C与B+A=C视为不同的等式(A,B,C>=0)

n根火柴棍必须全部用上

输入格式
一个整数n(n<=24)。

输出格式
一个整数,能拼成的不同等式的数目。

输入输出样例
输入
14
输出
2
输入
18
输出
9

思路:开一个数组,储存每个数字需要的火柴数。然后直接双重循环枚举a,b,然后a+b得到c,判断是否需要n和火柴即可。


import java.util.Scanner;


public class Main {
    static int[] count = {6,2,5,5,4,5,6,3,7,6};
    public static void main(String[] args) {
        Scanner sc =new Scanner(System.in);
        int n = sc.nextInt();
        int ans=0;
        for(int a = 0;a<=2000;a++){
            for(int b = 0;b<=2000;b++){
                int c = a+b;
                if(ok(a)+ok(b)+ok(c)+4==n){
                    ans++;
                }
            }
        }
        System.out.print(ans);
    }
    public static int ok(int x){
        int s = 0;
        if(x==0){
            return 6;
        }
        while(x>0){
            int t = x%10;
            s+=count[t];
            x/=10;
        }
        return s;
    }
}

6 涂国旗

题目描述
某国法律规定,只要一个由 N×M 个小方块组成的旗帜符合如下规则,就是合法的国旗。

从最上方若干行(至少一行)的格子全部是白色的;
接下来若干行(至少一行)的格子全部是蓝色的;
剩下的行(至少一行)全部是红色的;
现有一个棋盘状的布,分成了 N 行 M 列的格子,每个格子是白色蓝色红色之一,小 a 希望把这个布改成该国国旗,方法是在一些格子上涂颜料,盖住之前的颜色。

小a很懒,希望涂最少的格子,使这块布成为一个合法的国旗。

输入格式
第一行是两个整数 N,M。

接下来 N 行是一个矩阵,矩阵的每一个小方块是W(白),B(蓝),R(红)中的一个。

输出格式
一个整数,表示至少需要涂多少块。

输入输出样例
输入
4 5
WRWRW
BWRWB
WRWRW
RWBWR
输出
11
思路:枚举白色的终点,蓝色的终点,维护一个最小值即可。



import java.util.Scanner;

public class Main {
    static char[][] ch ;
    static int n,m,min;
    static int[] cut1,cut2,cut3;//白 蓝 红
    public static void main(String[] args) {
        Scanner sc =new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        ch =new char[n][];
        for(int i=0;i<n;i++){
            ch[i] = sc.next().toCharArray();
        }
        cut1 = new int[n];
        cut2 = new int[n];
        cut3 = new int[n];
        for(int i=0;i<n;i++){
            for(int j=0;j<ch[i].length;j++){
                if(ch[i][j]!='W'){
                    cut1[i]++;
                }
                if(ch[i][j]!='B'){
                    cut2[i]++;
                }
                if(ch[i][j]!='R'){
                    cut3[i]++;
                }
            }
        }
        int ans=100000000;
        int temp=0;
        for(int i=0;i<=n-3;i++){
            temp=0;
            for(int j=0;j<=i;j++){
                temp+=cut1[j];
            }
            for(int k=i+1;k<=n-2;k++){
                int t1 =temp;
                for(int y=i+1;y<=k;y++){
                    t1+=cut2[y];
                }
                for(int q=k+1;q<=n-1;q++){
                    t1+=cut3[q];
                }
                ans=Math.min(ans,t1);
            }
        }
        System.out.print(ans);
    }
}

7 [COCI2008-2009#2] PERKET

题目描述
Perket 是一种流行的美食。为了做好 Perket,厨师们必须小心选择配料,以便达到更好的口感。你有N种可支配的配料。对于每一种配料,我们知道它们各自的酸度 S 和甜度 B。当我们添加配料时,总的酸度为每一种配料的酸度总乘积;总的甜度为每一种配料的甜度的总和。

众所周知,美食应该口感适中;所以我们希望选取配料,以使得酸度和甜度的绝对差最小。

另外,我们必须添加至少一种配料,因为没有美食是以白水为主要配料的。

输入格式
第一行包括整数 N,表示可支配的配料数。

接下来 N 行,每一行为用空格隔开的两个整数,表示每一种配料的酸度和甜度。

输入数据保证,如果我们添加所有配料,总的酸度和甜度都不会超过 10^9。

输出格式
输出酸度和甜度的最小的绝对差。

输入输出样例
输入
1
3 10

输出
7

思路:dfs爆搜,对于每种配料,每次两种情况,选或者不选。维护一个最小值。



import java.util.Scanner;

import static java.lang.Math.abs;

public class Main {
    static int n,min=Integer.MAX_VALUE;
    static boolean flag ;
    static int[] arr;
    static long[][] book;
    public static void main(String[] args) {
        Scanner sc =new Scanner(System.in);
        n = sc.nextInt();
        arr = new int[n];
        book = new long[n][2];
        for(int i=0;i<n;i++){
            book[i][0]=sc.nextLong();
            book[i][1]=sc.nextLong();
        }
        dfs(0,0);
        System.out.print(min);
    }
    public static void dfs(int x,int y){
        if(x==n){
            long a=1;
            long b=0;
            flag =false;
            for(int i=0;i<n;i++){
                if(arr[i]==1){
                    flag=true;
                    a*=book[i][0];
                    b+=book[i][1];
                }
            }
            if(flag){
                long t =a-b;
                min = (int) Math.min(abs(t),min);
            }
            return ;
        }
        dfs(x+1,y);
        arr[x]=1;
        dfs(x+1,y+1);
        arr[x]=0;
    }
}

你可能感兴趣的:(算法初探)