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();
}
}