zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Java文件流应用:复制文件
    Java IO流之文件流
    初识Java-IO流
    Java集合之Properties
    Java之FilenameFilter接口
    Java之File类
    Java内部类
    Java常用类之要点总结
    Java异常类(Throwable)
    php分布式缓存系统 Memcached 入门
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822589.html
Copyright © 2011-2022 走看看