[NOIP2016提高组] 愤怒的小鸟
解题思路
state
表示,其中从右往左从
path[i][j]
为经过i,j
两点的抛物线状态,也是同样用二进制压缩,和state
类似,第
path
数组之后,我们对于每个状态的转移枚举图上的所有点,新状态也就是老状态和path[x][i]
作或运算,就可以很容易地进行转移了。
爆搜
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 20, M = 1 << 18;
int n, m, T, res, path[N][N], f[M];
struct Point { double x, y; } p[N];
int cmp(double a, double b)
{
if (fabs(a - b) <= 1e-6) return 0; // =
return 1;
}
void calc(int i, int j)
{
if (path[i][j]) return ;
double r1 = p[i].x, c1 = p[i].y;
double r2 = p[j].x, c2 = p[j].y;
double a = (c1 / r1 - c2 / r2) / (r1 - r2);
double b = c1 / r1 - a * r1;
if (a >= 0) return ; // 开口不朝下
if (!cmp(r1, r2)) return ; // 垂直点对
for (int k = 0; k < n; k ++ )
{
double rk = p[k].x, ck = p[k].y;
double t = rk * rk * a + b * rk;
if (!cmp(t, ck)) path[i][j] |= 1 << k;
}
path[j][i] = path[i][j];
}
void dfs(int st, int cnt)
{
if (f[st] <= cnt) return ;
if (st == (1 << n) - 1) return void (res = min(res, cnt));
if (cnt >= res - 1) return ;
int x;
for (x = 0; x < n; x ++ )
if (!(st >> x & 1)) break;
for (int i = 0; i < n; i ++ )
if (path[x][i] && cnt + 1 < res)
dfs(st | path[x][i], cnt + 1);
f[st] = min(f[st], cnt);
}
void solve()
{
memset(f, 0x3f, sizeof f);
memset(path, 0, sizeof path);
res = 2e9;
scanf("%d%d", &n, &m); // m 是无用条件
for (int i = 0; i < n; i ++ )
scanf("%lf%lf", &p[i].x, &p[i].y);
for (int i = 0; i < n; i ++ )
{
path[i][i] = 1 << i;
for (int j = i + 1; j < n; j ++ ) calc(i, j);
}
dfs(0, 0);
printf("%d\n", res);
}
int main()
{
scanf("%d", &T);
while (T -- ) solve();
return 0;
}
记忆化搜索
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define x first
#define y second
using namespace std;
using PDD = pair<double, double> ;
const int N = 20, M = 1 << 18;
int n, m, T, res, path[N][N];
PDD p[N];
int cmp(double a, double b)
{
return fabs(a - b) > 1e-6;
}
void calc(int i, int j)
{
if (path[i][j]) return ;
double r1 = p[i].x, c1 = p[i].y;
double r2 = p[j].x, c2 = p[j].y;
double a = (c1 / r1 - c2 / r2) / (r1 - r2);
double b = c1 / r1 - a * r1;
if (a >= 0) return ; // 开口不朝下
if (!cmp(r1, r2)) return ; // 垂直点对
for (int k = 0; k < n; k ++ )
{
double rk = p[k].x, ck = p[k].y;
double t = rk * rk * a + b * rk;
if (!cmp(t, ck)) path[i][j] |= 1 << k;
}
path[j][i] = path[i][j];
}
int dfs(int st, vector<int> &f)
{
int &v = f[st];
if (st == (1 << n) - 1) return 0;
if (v) return v;
int x;
for (x = 0; x < n; x ++ )
if ((st >> x & 1) == 0) break ;
int res = 2e9;
for (int i = 0; i < n; i ++ )
if (path[x][i]) res = min(dfs(st | path[x][i], f) + 1, res) ;
return v = res;
}
void solve()
{
res = 2e9;
vector<int> f(M, 0);
scanf("%d%d", &n, &m); // m 是无用条件
for (int i = 0; i < n; i ++ )
scanf("%lf%lf", &p[i].x, &p[i].y);
for (int i = 0; i < n; i ++ )
{
path[i][i] = 1 << i;
for (int j = i + 1; j < n; j ++ ) calc(i, j);
}
printf("%d\n", dfs(0, f));
memset(path, 0, sizeof path);
}
int main()
{
scanf("%d", &T);
while (T -- ) solve();
return 0;
}
个人实测爆搜会快一点,只要
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)