洛谷题单 算法1-3 暴力枚举
1 First Step (ファーストステップ)
题目背景
知らないことばかりなにもかもが(どうしたらいいの?)
一切的一切 尽是充满了未知数(该如何是好)
それでも期待で足が軽いよ(ジャンプだ!)
但我仍因满怀期待而步伐轻盈(起跳吧!)
温度差なんていつか消しちゃえってね
冷若冰霜的态度 有朝一日将会消失得无影无踪
元気だよ元気をだしていくよ
拿出活力 打起精神向前迈进吧
我们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的方格里找有多少个正方形,有多少长方形。这题对于数学菜鸡的我来说,真是不好做啊。。废活不多说,上图,先找规律。
看到这里,规律是不是呼之欲出啊,废话不多说,暴力就完了。
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;
}
}