• P2831 愤怒的小鸟 [状压dp/模拟退火]


    愤怒的小鸟


    Descriptionmathcal{Description}

    在第一象限给出NN个点, 要求使用最少的 y=ax2+bxy=ax^2+bx 抛物线覆盖所有点., (a<0a<0)

    N<=18N<=18


    Solutionmathcal{Solution}

    最初想法
    枚举第一个点 ii, 再枚举 xj>=xix_j>=x_i 的点 jj,
    i,ji,j 可以确定一条抛物线, 计算这条抛物线经过的点数,
    取经过点数最多的 i,ji,j 点对, 画出这条抛物线, 答案 +1,
    依次 贪心 下去, 得到答案.
    提交 => 45pts45pts

    数据范围 "N<=18""N<=18", 考虑 %你退火,
    最优方案一定可以排成一个排列, 可以分为连续的几部分, 每部分都可以被同一抛物线覆盖.
    于是问题就转化为 """寻找最优排列",
    %你退火 就可以了, 100ptscolor{red}{100pts}.


    正解部分
    其实这道题的正解是 dp状压dp,
    F[i]F[i] 表示 ii 状态时的最大值, p[j]p[j] 表示所有可能的抛物线,
    注意在预处理抛物线时, 避免同一抛物线重复计入抛物线数组, 相当于一个小优化.
    枚举 jj, 进行状态转移, F[ip[j]]=min(F[i]+1)F[i|p[j]] = min(F[i]+1)
    时间复杂度小于 O(TN22N)O(T*N^2*2^N),

    但是!
    O(TN22N)O(T*N^2*2^N) 并不是最优算法, 最优算法是 : 模拟退火
    待填坑.


    退 208ms模拟退火 208ms,dp 2608ms状压dp 2608ms (滑稽


    实现部分
    没什么好说的.


    Codemathcal{Code}

    贪心 代码  45pts↓ 45pts

    #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
    ", 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
    ", Ans);
    }
    
    int main(){
            int T;
            scanf("%d", &T);
            while(T --) Work();
            return 0;
    }
    

    退模拟退火 代码 100pts↓color{red}{100pts}

    #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
    ", 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
    ", Ans);
    }
    
    int main(){
            int T;
            scanf("%d", &T);
            while(T --) Work();
            return 0;
    }
    

    状压dp 代码 100ptscolor{red}{100pts}

    #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
    "); 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
    ", F[(1<<N)-1]);
    }
    
    int main(){
            int T;
            scanf("%d", &T);
            while(T --) Work();
            return 0;
    }
    
  • 相关阅读:
    FZU 2112 并查集、欧拉通路
    HDU 5686 斐波那契数列、Java求大数
    Codeforces 675C Money Transfers 思维题
    HDU 5687 字典树插入查找删除
    HDU 1532 最大流模板题
    HDU 5384 字典树、AC自动机
    山科第三届校赛总结
    HDU 2222 AC自动机模板题
    HDU 3911 线段树区间合并、异或取反操作
    CodeForces 615B Longtail Hedgehog
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822589.html
Copyright © 2020-2023  润新知