P2831 愤怒的小鸟 [状压dp/模拟退火]
在第一象限给出个点, 要求使用最少的 抛物线覆盖所有点., ()
枚举第一个点 , 再枚举 的点 ,
可以确定一条抛物线, 计算这条抛物线经过的点数,
取经过点数最多的 点对, 画出这条抛物线, 答案 +1,
依次 贪心 下去, 得到答案.
提交 => …
数据范围 , 考虑 %你退火,
最优方案一定可以排成一个排列, 可以分为连续的几部分, 每部分都可以被同一抛物线覆盖.
于是问题就转化为 ,
跑 %你退火 就可以了, .
其实这道题的正解是 ,
设 表示 状态时的最大值, 表示所有可能的抛物线,
注意在预处理抛物线时, 避免同一抛物线重复计入抛物线数组, 相当于一个小优化.
枚举 , 进行状态转移,
时间复杂度小于 ,
但是!
并不是最优算法, 最优算法是 : 模拟退火
待填坑.
, (滑稽
没什么好说的.
代码
#include<bits/stdc++.h>
#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;
}
代码
#include<bits/stdc++.h>
#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 代码
#include<bits/stdc++.h>
#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;
}