深度优先搜索

P1219 [USACO1.5] 八皇后 Checker Challenge

题目描述

一个如下的 6×66×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

上面的布局可以用序列 2 4 6 1 3 52 4 6 1 3 5 来描述,第 ii 个数字表示在第 ii 行的相应位置有一个棋子,如下:

行号 1 2 3 4 5 61 2 3 4 5 6

列号 2 4 6 1 3 52 4 6 1 3 5

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 33 个解。最后一行是解的总个数。

输入格式

一行一个正整数 nn,表示棋盘是 n×nn×n 大小的。

输出格式

前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

输入输出样例

输入 #1复制

6

输出 #1复制

2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

说明/提示

【数据范围】
对于 100%100% 的数据,6≤n≤136≤n≤13。

题目翻译来自NOCOW。

USACO Training Section 1.5


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
	static int a[] = new int[100];// 答案
	static int b[] = new int[100];// 列
	static int c[] = new int[100];// 右
	static int d[] = new int[100];// 左
	static int total = 0;
	static int n;
	static BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));

	public static void dfs(int i) {
		if (i > n) {
			if (total <= 2) {
				for (int k = 1; k <= n; k++) {
					System.out.print(a[k] + " ");
				}
				System.out.println();
			}
			total++;
		}
		for (int j = 1; j <= n; j++) {
			if (b[j] == 0 && c[i + j] == 0 && d[i - j + n] == 0) {
				a[i] = j;
				b[j] = 1;
				c[i + j] = 1;
				d[i - j + n] = 1;
				dfs(i + 1);
				b[j]=0;
				c[i + j] = 0;
				d[i - j + n] = 0;
			}
		}
	}

	

	public static void main(String args[]) throws IOException {
		n = Integer.parseInt(bf.readLine());
		dfs(1);
		System.out.println(total);
	}
}

P1019 [NOIP2000 提高组] 单词接龙

题目背景

注意:本题为上古 NOIP 原题,不保证存在靠谱的做法能通过该数据范围下的所有数据。

本题为搜索题,本题不接受 hack 数据。关于此类题目的详细内容

NOIP2000 提高组 T3

题目描述

单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beast 和 astonish,如果接成一条龙则变为 beastonish,另外相邻的两部分不能存在包含关系,例如 at 和 atide 间不能相连。

输入格式

输入的第一行为一个单独的整数 nn 表示单词数,以下 nn 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。

输出格式

只需输出以此字母开头的最长的“龙”的长度。

输入输出样例

输入 #1复制

5
at
touch
cheat
choose
tact
a

输出 #1复制

23

说明/提示

样例解释:连成的“龙”为 atoucheatactactouchoose

n≤20n≤20。

import java.util.Scanner;

public class Main {
    static String[] word = new String[10010]; // 存储输入的字符串
    static int[][] g = new int[1010][1010]; // 记录字符串之间的连接长度
    static int[] use = new int[1010]; // 记录字符串使用次数
    static char head; // 起始字符
    static int n; // 字符串数量
    static int ans = 0; // 最大连接字符串长度
    
    // 递归求解能够连接的字符串的最大长度
    public static void dfs(String dragon, int last) {
        ans = Math.max(ans, dragon.length()); // 更新最大长度
        use[last]++; // 标记字符串使用次数加一
        for (int i = 1; i <= n; i++) {
            if (g[last][i] != 0 && use[i] < 2) { // 判断字符串可以连接且未使用超过2次
                dfs(dragon + word[i].substring(g[last][i]), i); // 递归连接字符串
            }
        }
        use[last]--; // 回溯
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt(); // 读取字符串数量
        for (int i = 1; i <= n; i++) {
            word[i] = scanner.next(); // 读取每个字符串
        }
        head = scanner.next().charAt(0); // 读取起始字符
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                String a = word[i];
                String b = word[j];
                for (int k = 1; k < Math.min(a.length(), b.length()); k++) {
                    if (a.substring(a.length() - k).equals(b.substring(0, k))) {
                        g[i][j] = k; // 计算字符串之间连接长度
                        break;
                    }
                }
            }
        }

        for (int i = 1; i <= n; i++) {
            if (word[i].charAt(0) == head) { // 查找起始字符相同的字符串
                dfs(word[i], i); // 开始递归连接字符串
            }
        }
        
        System.out.println(ans); // 输出最大连接字符串长度
    }
}

P5194 [USACO05DEC] Scales S

题目描述

约翰有一架用来称牛的体重的天平。与之配套的是 N (1≤N≤1000)N (1≤N≤1000) 个已知质量的砝码(所有砝码质量的数值都在 3232 位带符号整数范围内)。

每次称牛时,他都把某头奶牛安置在天平的某一边,然后往天平另一边加砝码,直到天平平衡,于是此时砝码的总质量就是牛的质量(约翰不能把砝码放到奶牛的那边,因为奶牛不喜欢称体重,每当约翰把砝码放到她的蹄子底下,她就会尝试把砝码踢到约翰脸上)。

天平能承受的物体的质量不是无限的,当天平某一边物体的质量大于 C (1≤C≤230)C (1≤C≤230) 时,天平就会被损坏。砝码按照它们质量的大小被排成一行。并且,这一行中从第 33 个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和。

约翰想知道,用他所拥有的这些砝码以及这架天平,能称出的质量最大是多少。由于天平的最大承重能力为 CC,他不能把所有砝码都放到天平上。

现在约翰告诉你每个砝码的质量,以及天平能承受的最大质量,你的任务是选出一些砝码,使它们的质量和在不压坏天平的前提下是所有组合中最大的。

输入格式

第 11 行输入两个用空格隔开的正整数 NN 和 CC。

第 22 到 N+1N+1 行:每一行仅包含一个正整数,即某个砝码的质量。保证这些砝码的质量是一个不下降序列。

输出格式

输出一个正整数,表示用所给的砝码能称出的不压坏天平的最大质量。

输入输出样例

输入 #1复制

3 15
1
10
20

输出 #1复制

11
import java.util.Scanner;

public class Main {
    static long[] sum;
    static long[] a;
    static long ans;
    static long n;
    static long c;

    static void dfs(int cur, long x) {
        if (x > c) return;
        if (sum[cur - 1] + x <= c) {
            ans = Math.max(ans, sum[cur - 1] + x);
            return;
        }
        ans = Math.max(ans, x);
        for (int i = 1; i < cur; i++)
            dfs(i, x + a[i]);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextLong();
        c = scanner.nextLong();
        sum = new long[1005];
        a = new long[1005];
        ans = 0;
        for (int i = 1; i <= n; i++) {
            a[i] = scanner.nextLong();
            sum[i] = sum[i - 1] + a[i];
        }
        dfs((int) (n + 1), 0);
        System.out.println(ans);
    }
}

P5440 【XR-2】奇迹

题目背景

相信奇迹的人,本身就和奇迹一样了不起。——笛亚 《星游记》

题目描述

我们称一个日期为一个八位数,第 1~4 位构成年,第 5~6 位构成月,第 7~8 位构成日,不足位数用 0 补足。同时,要求日期所代表的这一天真实存在,且年的范围为 1~9999。

出现奇迹的日期都存在相同的特点:由“日”组成的两位数,由“月+日”组成的四位数,由“年+月+日”组成的八位数均为质数。但并不是所有存在这样特点的日期都一定会出现奇迹。

现在,你得到了一个可能会出现奇迹的日期,然而不幸的是这个日期却是残缺的,八位中可能有若干位无法确定。你需要知道这个日期有多少种可能,这样你才能做好充足的准备去迎接奇迹的到来。

输入格式

本题有多组数据。

第一行一个正整数 TT,表示数据组数。

接下来的 TT 行,每行一个八位字符串。其中第 ii 位如果为 -,则表示日期的第 ii 位无法确定,否则表示日期的第 ii 位为字符串中第 ii 位上的数字。

输出格式

对每组数据,一行一个整数,表示答案。

输入输出样例

输入 #1复制

2
53-7-3-7
20190629

输出 #1复制

6
0

说明/提示

【样例 11 说明】

53-7-3-7 的 66 种可能的日期如下:

53070307
53070317
53170307
53370307
53570317
53770307

【数据规模与约定】

一共 1010 个测试点,记 cc 为八位字符串中 - 的个数。

对前 99 个测试点,在第 ii 个测试点中保证 c=i−1c=i−1。

对 100%100% 的数据保证 1≤T≤101≤T≤10。

import java.util.*;

public class Main {
    static final int[] p = {0, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
    static final int[] d = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    static List<Integer> a = new ArrayList();
    static List<Integer> ans = new ArrayList();

    public static void main(String[] args) {
        precompute();
        Scanner sc = new Scanner(System.in);
        int T = sc.nextInt();
        while (T-- > 0) {
            String s = sc.next();
            int count = 0;
            for (int i = 0; i < ans.size(); ++i) {
                int now = ans.get(i);
                boolean flag = true;
                for (int j = 7; flag && j >= 0; j--, now /= 10) {
                    if (s.charAt(j) != '-' && s.charAt(j) - '0' != now % 10) {
                        flag = false;
                    }
                }
                count += flag ? 1 : 0;
            }
            System.out.println(count);
        }
        sc.close();
    }

    static void precompute() {
        for (int i = 1; i <= 12; ++i) {
            for (int j = 1; p[j] <= d[i]; ++j) {
                if (isPrime(i * 100 + p[j])) {
                    a.add(i * 100 + p[j]);
                }
            }
        }

        for (int i = 4; i <= 9999; i += 4) {
            if ((i % 100 != 0 || i % 400 == 0) && isPrime(i * 10000 + 229)) {
                ans.add(i * 10000 + 229);
            }
        }

        for (int i = 1; i <= 9999; ++i) {
            for (int value : a) {
                if (isPrime(i * 10000 + value)) {
                    ans.add(i * 10000 + value);
                }
            }
        }
    }

    static boolean isPrime(int x) {
        if (x < 2) return false;
        for (int i = 2; i * i <= x; ++i) {
            if (x % i == 0) return false;
        }
        return true;
    }
}

P1378 油滴扩展

题目描述

在一个长方形框子里,最多有 NN 个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界。必须等一个油滴扩展完毕才能放置下一个油滴。那么应该按照怎样的顺序在这 NN 个点上放置油滴,才能使放置完毕后所有油滴占据的总面积最大呢?(不同的油滴不会相互融合)

注:圆的面积公式 S=πr2S=πr2,其中 rr 为圆的半径。

输入格式

第一行,一个整数 NN。

第二行,四个整数 x,y,x′,y′x,y,x′,y′,表示长方形边框一个顶点及其对角顶点的坐标。

接下来 NN 行,第 ii 行两个整数 xi,yixi​,yi​,表示盒子内第 ii 个点的坐标。

输出格式

一行,一个整数,长方形盒子剩余的最小空间(结果四舍五入输出)。

输入输出样例

输入 #1复制

2
20 0 10 10
13 3
17 7

输出 #1复制

50

说明/提示

对于 100%100% 的数据,1≤N≤61≤N≤6,坐标范围在 [−1000,1000][−1000,1000] 内。

import java.util.Scanner;

public class Main {
    static final int maxn = 10; // 最大点数
    static final double PI = 3.1415926535; // 圆周率
    static boolean[] s = new boolean[maxn]; // 标记数组,标记点是否已经被选中
    static double[] x = new double[maxn]; // x 坐标数组
    static double[] y = new double[maxn]; // y 坐标数组
    static double[] r = new double[maxn]; // 半径数组
    static double xa, ya, xb, yb, ansmax; // 矩形坐标和最大面积
    static int n; // 点的个数

    // 计算第 i 个点的半径
    static double cal(int i) {
        // 计算到两个矩形边的最小距离
        double s1 = Math.min(Math.abs(x[i] - xa), Math.abs(x[i] - xb));
        double s2 = Math.min(Math.abs(y[i] - ya), Math.abs(y[i] - yb));
        double ans = Math.min(s1, s2);
        // 考虑其他已经选择的圆对当前圆的影响
        for (int j = 1; j <= n; j++) {
            if (i != j && s[j]) {
                double d = Math.sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
                ans = Math.min(ans, Math.max(d - r[j], 0.0));
            }
        }
        return ans;
    }

    // 递归搜索所有可能的情况
    static void dfs(int k, double sum) {
        if (k > n) {
            ansmax = Math.max(ansmax, sum);
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (!s[i]) {
                r[i] = cal(i);
                s[i] = true;
                dfs(k + 1, sum + r[i] * r[i] * PI);
                s[i] = false;
            }
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        double ss; // 矩形的面积
        n = scanner.nextInt(); // 输入点的个数
        xa = scanner.nextDouble(); // 输入矩形 A 的坐标
        ya = scanner.nextDouble();
        xb = scanner.nextDouble(); // 输入矩形 B 的坐标
        yb = scanner.nextDouble();
        ss = Math.abs(xa - xb) * Math.abs(ya - yb); // 计算矩形面积
        for (int i = 1; i <= n; i++) {
            x[i] = scanner.nextDouble(); // 输入每个点的坐标
            y[i] = scanner.nextDouble();
        }
        dfs(1, 0); // 开始搜索
        System.out.println((int) (ss - ansmax + 0.5)); // 输出结果
        scanner.close();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值