AcWing 524. 愤怒的小鸟
题目链接:
https://www.acwing.com/problem/content/description/526/
题解:
记忆化搜索比较好理解
状态转移DP只对全1情况有最优解
AC代码:
记忆化搜索
// 记忆化搜索
import java.util.*;
public class Main {
static int N = 20, M = 1 << 18;
static PII[] pr = new PII[N];
static int[][] path = new int[N][N];
static int[] f = new int[M];
static int n, m;
static void init() {
Arrays.fill(f, 0x3f3f3f3f);
for (int i = 0; i < N; i ++) Arrays.fill(path[i], 0);
}
static int cmp(double a, double b) {
if (Math.abs(a - b) < 1e-6) return 0;
if (a > b) return 1;
return -1;
}
// 含义是,从st状态转移到全1状态所需的最小步数
static int dfs(int st) {
if (st == (1 << n) - 1) return 0;
if (f[st] != 0x3f3f3f3f) return f[st];
int k = -1;
for (int i = 0; i < n; i ++) {
if ((st >> i & 1) == 0) {
k = i;
break;
}
}
for (int j = 0; j < n; j ++) {
if ((path[k][j] & 1 << k) != 0)
f[st] = Math.min(f[st], dfs(st | path[k][j]) + 1);
}
return f[st];
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int T = sc.nextInt();
while(T -- > 0) {
init();
n = sc.nextInt();
m = sc.nextInt();
for (int i = 0; i < n; i ++) pr[i] = new PII(sc.nextDouble(), sc.nextDouble());
for (int i = 0; i < n; i ++) {
path[i][i] = 1 << i;
for (int j = 0; j < n; j ++) {
double x1 = pr[i].x, y1 = pr[i].y;
double x2 = pr[j].x, y2 = pr[j].y;
if (cmp(x1, x2) == 0) continue;
double a = (y1 / x1 - y2 / x2) / (x1 - x2);
if (cmp(a, 0.0) >= 0) continue;
double b = (y1 / x1) - (a * x1);
for (int k = 0; k < n; k ++) {
double x3 = pr[k].x, y3 = pr[k].y;
double yy = a * x3 * x3 + b * x3;
if (cmp(yy, y3) == 0) path[i][j] = path[i][j] | 1 << k;
}
}
}
int res = dfs(0);
System.out.println(res);
}
}
}
class PII {
double x, y;
PII(double x, double y) {
this.x = x;
this.y = y;
}
}
状态转移DP
// 状态转移DP
import java.util.*;
public class Main {
static int N = 20, M = 1 << 18;
static PII[] pr = new PII[N];
static int[][] path = new int[N][N];
// f[st]的含义:从0转移到状态st的所需的最小步数,只对全1的情况下成立最优解
static int[] f = new int[M];
static int n, m;
static void init() {
Arrays.fill(f, 0x3f3f3f3f);
for (int i = 0; i < N; i ++) Arrays.fill(path[i], 0);
}
static int cmp(double a, double b) {
if (Math.abs(a - b) < 1e-6) return 0;
if (a > b) return 1;
return -1;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int T = sc.nextInt();
while(T -- > 0) {
init();
n = sc.nextInt();
m = sc.nextInt();
for (int i = 0; i < n; i ++) pr[i] = new PII(sc.nextDouble(), sc.nextDouble());
for (int i = 0; i < n; i ++) {
path[i][i] = 1 << i;
for (int j = 0; j < n; j ++) {
double x1 = pr[i].x, y1 = pr[i].y;
double x2 = pr[j].x, y2 = pr[j].y;
if (cmp(x1, x2) == 0) continue;
double a = (y1 / x1 - y2 / x2) / (x1 - x2);
if (cmp(a, 0.0) >= 0) continue;
double b = (y1 / x1) - (a * x1);
for (int k = 0; k < n; k ++) {
double x3 = pr[k].x, y3 = pr[k].y;
double yy = a * x3 * x3 + b * x3;
if (cmp(yy, y3) == 0) path[i][j] = path[i][j] | 1 << k;
}
}
}
f[0] = 0;
for (int i = 0; i < (1 << n) - 1; i ++) {
// 因为是任意选择一个未被覆盖的,所以只对全1的情况下成立最优解,其他状态不保证
int k = -1;
for (int j = 0; j < n; j ++) {
if ((i & 1 << j) == 0) {
k = j;
break;
}
}
for (int u = 0; u < n; u ++) {
if (f[i] != 0x3f3f3f3f)
f[i | path[k][u]] = Math.min(f[i | path[k][u]], f[i] + 1);
}
}
System.out.println(f[(1 << n) - 1]);
}
}
}
class PII {
double x, y;
PII(double x, double y) {
this.x = x;
this.y = y;
}
}