UVA - 1543 Telescope (区间dp)

问题

单位圆上有n个点,在其中选择m个,使得连成的凸多边形面积最大,输出面积

分析

区间dp,我的想法是dp[k][i][j]是区间(i,j)中选择k的点,区间两端的点不计入k中,用dp[k][i][j]表示选择点后裁下来的弓形大小,对应角度大小 α 0 < = α < = 2 π ) \alpha(0<=\alpha<=2\pi)) 的弓形面积是 0.5 ( α s i n ( α ) ) 0.5*(\alpha-sin(\alpha)) ,遍历i,j,可以使用递归记忆化搜索来计算DFS(m-2,i,j)

#include<iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=42,INF=0x3f3f3f3f;
double dp[maxn][maxn][maxn],p[maxn],t;
int n,m;

//x是弓形对应的角度
double AreaofArch(double x){
    return 0.5*(x-sin(x));  //扇形面积公式:S=0.5*alpha*r^2
}
//递归记忆化搜索
//dp[k][i][j]意味着要在(i,j)中确定k个点,期间切下来的最小弓形面积,1<=i<j<=n,0<=k<=m-2
double DFS(int k,int i,int j){
    double &ans=dp[k][i][j];
//    if(j-i<=k) return ans;  //可以不需要这一句,如果出现k小于区间里面的所有点数,那么会返回INF
    if(ans<=1) return ans;
    if(k==0) return ans=AreaofArch(p[j]-p[i]);  //不需要再确定了
    for(int h=i+1;h<=j-k;++h){
        ans=min(ans,AreaofArch(p[h]-p[i])+DFS(k-1,h,j));
    }
    return ans;
}

int main(void){
    while(cin>>n>>m && n){
        for(int i=1;i<=n;++i){
            scanf("%lf",&t);
            p[i]=2*M_PI*t;  //换算成角度
        }
        for(int k=0;k<=m-2;++k){
            for(int i=1;i<=n;++i){
                for(int j=1;j<=n;++j){
                    dp[k][i][j]=INF;
                }
            }
        }

        double ans=INF;
        for(int i=1;i<=n-m+1;++i){
            for(int j=i+m-1;j<=n;++j){
                ans=min(ans,AreaofArch(2*M_PI-p[j]+p[i])+DFS(m-2,i,j));
            }
        }
        printf("%.6lf\n",M_PI-ans);  //圆面积减去弓形面积
    }
}

不使用递归记忆化搜索,采用从下向上迭代

#include<iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=42,INF=0x3f3f3f3f;
double dp[maxn][maxn][maxn],p[maxn],t;
int n,m;

//x是弓形对应的角度
double AreaofArch(double x){
    return 0.5*(x-sin(x));  //扇形面积公式:S=0.5*alpha*r^2
}
//递归记忆化搜索
//dp[k][i][j]意味着要在(i,j)中确定k个点,期间切下来的最小弓形面积,1<=i<j<=n,0<=k<=m-2
//double DFS(int k,int i,int j){
//    double &ans=dp[k][i][j];
////    if(j-i<=k) return ans;  //可以不需要这一句,如果出现k小于区间里面的所有点数,那么会返回INF
//    if(ans<=1) return ans;
//    if(k==0) return ans=AreaofArch(p[j]-p[i]);  //不需要再确定了
//    for(int h=i+1;h<=j-k;++h){
//        ans=min(ans,AreaofArch(p[h]-p[i])+DFS(k-1,h,j));
//    }
//    return ans;
//}

int main(void){
    while(cin>>n>>m && n){
        for(int i=1;i<=n;++i){
            scanf("%lf",&t);
            p[i]=2*M_PI*t;
        }
        for(int i=1;i<=n;++i){
            for(int j=i+1;j<=n;++j){
                dp[0][i][j]=AreaofArch(p[j]-p[i]);
            }
        }
        for(int k=1;k<=m-2;++k){  //遍历点数区间大小
            for(int i=1;i<=n-k-1;++i){  //区间是(i,j),在其中选k个点
                for(int j=i+k+1;j<=n;++j){
                    double &temp=dp[k][i][j];
                    temp=INF;
                    for(int h=i+1;h<=j-k;++h){
                        temp=min(temp,AreaofArch(p[h]-p[i])+dp[k-1][h][j]);
                    }
                }
            }
        }
        double ans=INF;
        for(int i=1;i<=n-m+1;++i){
            for(int j=i+m-1;j<=n;++j){
                ans=min(ans,AreaofArch(2*M_PI-p[j]+p[i])+dp[m-2][i][j]);
            }
        }
        printf("%.6lf\n",M_PI-ans);
    }
}

发布了50 篇原创文章 · 获赞 0 · 访问量 701

猜你喜欢

转载自blog.csdn.net/zpf1998/article/details/104125166