愤 怒 的 小 鸟 愤怒的小鸟 愤怒的小鸟
D e s c r i p t i o n \mathcal{Description} Description
在第一象限给出 N N N个点, 要求使用最少的 y = a x 2 + b x y=ax^2+bx y=ax2+bx 抛物线覆盖所有点., ( a < 0 a<0 a<0)
N < = 18 N<=18 N<=18
S o l u t i o n \mathcal{Solution} Solution
最 初 想 法 最初想法 最初想法
枚举第一个点 i i i, 再枚举 x j > = x i x_j>=x_i xj>=xi 的点 j j j,
i , j i,j i,j 可以确定一条抛物线, 计算这条抛物线经过的点数,
取经过点数最多的 i , j i,j i,j 点对, 画出这条抛物线, 答案 +1,
依次 贪心 下去, 得到答案.
提交 => 45 p t s 45pts 45pts…
数据范围 " N < = 18 " "N<=18" "N<=18", 考虑 %你退火,
最优方案一定可以排成一个排列, 可以分为连续的几部分, 每部分都可以被同一抛物线覆盖.
于是问题就转化为 " 寻 找 最 优 排 列 " "寻找最优排列" "寻找最优排列",
跑 %你退火 就可以了, 100 p t s \color{red}{100pts} 100pts.
正 解 部 分 正解部分 正解部分
其实这道题的正解是 状 压 d p 状压dp 状压dp,
设 F [ i ] F[i] F[i] 表示 i i i 状态时的最大值, p [ j ] p[j] p[j] 表示所有可能的抛物线,
注意在预处理抛物线时, 避免同一抛物线重复计入抛物线数组, 相当于一个小优化.
枚举 j j j, 进行状态转移, F [ i ∣ p [ j ] ] = m i n ( F [ i ] + 1 ) F[i|p[j]] = min(F[i]+1) F[i∣p[j]]=min(F[i]+1)
时间复杂度小于 O ( T ∗ N 2 ∗ 2 N ) O(T*N^2*2^N) O(T∗N2∗2N),
但是!
O ( T ∗ N 2 ∗ 2 N ) O(T*N^2*2^N) O(T∗N2∗2N) 并不是最优算法, 最优算法是 : 模拟退火
待填坑.
模 拟 退 火 208 m s 模拟退火\ 208ms 模拟退火 208ms, 状 压 d p 2608 m s 状压dp\ 2608ms 状压dp 2608ms (滑稽
实 现 部 分 实现部分 实现部分
没什么好说的.
C o d e \mathcal{Code} Code
贪 心 贪心 贪心 代码 ↓ 45 p t s ↓\ 45pts ↓ 45pts
#include
#define reg register
int N;
int M;
bool Used[25];
struct Bird{ double x, y; } A[25];
bool cmp(Bird a, Bird b){ return a.x < b.x; }
void Calc(int i, int j, double &a, double &b){
double k1 = A[i].x*A[i].x, k2 = A[i].x;
double k3 = A[j].x*A[j].x, k4 = A[j].x;
double k5 = k3/k4 * k2 - k1, y3 = A[j].y/k4 * k2 - A[i].y;
a = y3/k5, b = (A[i].y-k1*a) / k2;
}
void Work(){
memset(Used, 0, sizeof Used);
int Ans = 0;
scanf("%d%d", &N, &M);
for(reg int i = 1; i <= N; i ++) scanf("%lf%lf", &A[i].x, &A[i].y);
if(N == 1){ printf("%d\n", 1); return ; }
for(reg int i = 1; i <= N; i ++){
if(Used[i]) continue ;
int max_cnt = 0, id = 0;
for(reg int j = i+1; j <= N; j ++){
if(Used[j]) continue ;
int cnt = 0;
double a, b;
Calc(i, j, a, b);
if(a > 1e-14 || fabs(a) < 1e-14) continue ;
for(reg int k = 1; k <= N; k ++){
if(k == i || Used[k]) continue ;
double a1, b1;
Calc(i, k, a1, b1);
if(fabs(a1-a) < 1e-14 && fabs(b1-b) < 1e-14) cnt ++;
}
if(cnt > max_cnt) max_cnt = cnt, id = j;
}
if(id){
double a, b;
Calc(i, id, a, b);
for(reg int k = 1; k <= N; k ++){
if(k == i || Used[k]) continue ;
double a1, b1;
Calc(i, k, a1, b1);
if(fabs(a1-a) < 1e-14 && fabs(b1-b) < 1e-14) Used[k] = 1;
}
}
Used[i] = 1;
Ans ++;
}
printf("%d\n", Ans);
}
int main(){
int T;
scanf("%d", &T);
while(T --) Work();
return 0;
}
模 拟 退 火 模拟退火 模拟退火 代码 ↓ 100 p t s ↓\color{red}{100pts} ↓100pts
#include
#define reg register
const int inf = 0x3f3f3f3f;
const int maxn = 25;
const double eps = 1e-14;
int N;
int M;
int Ans;
int tmp[maxn];
struct Bird{ double x, y; } A[25];
bool cmp(Bird a, Bird b){ return a.x < b.x; }
void Calc(int i, int j, double &a, double &b){
double k1 = A[i].x*A[i].x, k2 = A[i].x;
double k3 = A[j].x*A[j].x, k4 = A[j].x;
double k5 = k3/k4 * k2 - k1, y3 = A[j].y/k4 * k2 - A[i].y;
a = y3/k5, b = (A[i].y-k1*a) / k2;
}
int Play(){
int s = 0;
double a = 0, b = 0;
for(reg int i = 1; i <= N; i ++){
int t1 = tmp[i], t2 = tmp[i+1];
if(fabs(a) > eps && fabs( a*A[t1].x*A[t1].x + b*A[t1].x - A[t1].y ) < eps ) continue ;
s ++; if(i == N) break ;
if(fabs(A[t1].x - A[t2].x) < eps) continue ;
Calc(t1, t2, a, b);
if(a > eps || fabs(a) < eps) a = b = 0;
else i ++;
}
return s;
}
void SA(){
int res = Ans;
double T = 250, delt = 0.99;
while(T > 1e-6){
int pos_1 = (rand()%N)+1, pos_2 = (rand()%N) + 1;
while(pos_1 == pos_2) pos_2 = (rand()%N) + 1;
std::swap(tmp[pos_1], tmp[pos_2]);
int New_ans = Play();
int Temp = New_ans - res;
if(tmp < 0 || exp(-Temp/T)*RAND_MAX > rand()) res = New_ans, Ans = std::min(res, Ans);
else std::swap(tmp[pos_1], tmp[pos_2]);
T *= delt;
}
}
void Work(){
scanf("%d%d", &N, &M);
for(reg int i = 1; i <= N; i ++) scanf("%lf%lf", &A[i].x, &A[i].y);
if(N == 1){ printf("%d\n", 1); return ; }
for(reg int i = 1; i <= N; i ++) tmp[i] = i;
srand(92332322), srand(rand());
std::random_shuffle(tmp+1, tmp+N+1);
Ans = Play();
for(reg int i = 1; i <= 5; i ++) SA();
printf("%d\n", Ans);
}
int main(){
int T;
scanf("%d", &T);
while(T --) Work();
return 0;
}
状压dp 代码 ↓ ↓ ↓ 100 p t s \color{red}{100pts} 100pts
#include
#define reg register
const double eps = 1e-14;
int N;
int M;
int F[1<<19];
int p[1<<19];
struct Bird{ double x, y; } A[25];
void Calc(int i, int j, double &a, double &b){
double k1 = A[i].x*A[i].x, k2 = A[i].x;
double k3 = A[j].x*A[j].x, k4 = A[j].x;
double k5 = k3/k4 * k2 - k1, y3 = A[j].y/k4 * k2 - A[i].y;
a = y3/k5, b = (A[i].y-k1*a) / k2;
}
void Work(){
scanf("%d%d", &N, &M);
int p_cnt = 0;
for(reg int i = 1; i <= N; i ++) scanf("%lf%lf", &A[i].x, &A[i].y);
if(N == 1){ printf("1\n"); return ; }
memset(F, 0x3f, sizeof F), F[0] = 0;
for(reg int i = 1; i <= N; i ++){
int tmp = 0, last = p_cnt;
for(reg int j = i+1; j <= N; j ++){
if((tmp>>j-1) & 1) continue ;
double a, b;
if(fabs(A[i].x-A[j].x) < eps) continue ;
Calc(i, j, a, b);
if(a > eps || fabs(a) < eps) continue ;
p[++ p_cnt] = (1 << i-1) | (1 << j-1);
for(reg int k = 1; k <= N; k ++)
if(fabs(A[k].x*A[k].x*a + A[k].x*b - A[k].y) < eps) p[p_cnt] |= 1 << k-1;
tmp |= p[p_cnt];
}
if(p_cnt == last) p[++ p_cnt] = 1 << i-1;
}
for(reg int i = 0; i < (1<<N); i ++)
for(reg int j = 1; j <= p_cnt; j ++)
F[i|p[j]] = std::min(F[i|p[j]], F[i] + 1);
printf("%d\n", F[(1<<N)-1]);
}
int main(){
int T;
scanf("%d", &T);
while(T --) Work();
return 0;
}