问题
单位圆上有n个点,在其中选择m个,使得连成的凸多边形面积最大,输出面积
分析
区间dp,我的想法是dp[k][i][j]是区间(i,j)中选择k的点,区间两端的点不计入k中,用dp[k][i][j]表示选择点后裁下来的弓形大小,对应角度大小 的弓形面积是 ,遍历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);
}
}