合并类动态规划专题训练

话说,我已经很久没有写博客了,其实里面有很大一段的故事的。。。
不管了,现在都已经11号了,还有7天考试,真是着实不爽啊!
好了,回到正题了,这周我们练了一个合并类的动态规划:
0多边形【推荐】
1 【NOIP动态规划专题】等腰三角形
2 【NOIP动态规划专题】能量项链
3 行政划分【难】

0 多边形【推荐】

  多边形是一个单人玩的游戏,开始时有一个N个顶点的多边形。如图,这里N=4。每个顶点有一个整数标记,每条边上有一个“+”号或“*”号。边从1编号到N。
  第一步,一条边被拿走;随后各步包括如下:
  选择一条边E和连接着E的两个顶点V1和 V2;
  得到一个新的顶点,标记为V1与V2通过边E上的运算符运算的结果。
  最后,游戏中没有边,游戏的得分为仅剩余的一个顶点的值。
  写一个程序,对于给定一个多边形,计算出可能的最高得分以及操作方法。
  【数据范围】
  3<=N<=50
  不管怎么操作,中间结果一定在[-32768,32767]范围内。

分析

好了对于这题,我们可以先枚举一条被删的边,然后我们就可以把这个多边形展开
这是很重要的思想:一个图形很难dp,而把它变成一列点便变得很好dp了。
于是我们设f[i,j]表示已经计算出第i个点到第j个点的操作最大值,
明显的f[i,j]=max(f[i,k]&f[k,j])&表示计算的符号。
那么这样就完成了吗?很明显的不是,因为这题里面有负数!
有负数就意味着两个负数的积有可能超过两个正数的积,所以在这里我们再设:
g[i,j]表示最小的值。
转移都差不多的。

【NOIP动态规划专题】等腰三角形

给定一个正N边形,可以通过连线将这个多边形分割成N-2个三角形,问这N-2个三角形中恰有k个等腰三角形的分割方法有多少?这个值可能很大,输出对9397取模的结果。

分析

其实上面的题目就是这题的基础,我们也一样把点都展开来,那么方程应该是:
f[i,j,k]表示i到j的点形成k个等腰三角形的方案数。
f[i,j,k]=l<jl=i+1r<=kr=0f[i,l,r]f[l,j,krpd()]
这里的方程和上面唯一不一样的是减了一个pd(),这个是指有可能l和i和j的连线会形成一个等腰三角形。
而情况请读者自己考虑。

【NOIP动态规划专题】能量项链

比较水,不讲了。

行政划分【难】

这题是重点!
某国领土形状十分奇特,可以将它近似地看作是一个有N 个顶点的凸多边形 。现在该国政府想要将它划分为(N-2)个互不重叠的行政区域,并希望每个区域的形状都是三角形,三角形的顶点即为凸多边形的顶点。该国政府又出于资源、人口、宗教等多方面的考虑,希
望得到一种划分方案,使得(N-2)块区域的面积的方差最小
这里写图片描述
现在该国政府官员找到了你,希望你能够帮助他们解决这个问题。

Input

第一行是一个整数N (4≤N≤50), 表示凸多边形顶点的个数
接下来的N 行,每行有两个实数X、Y(-1000000≤X、Y≤1000000),分别表示按比例缩小后的凸多边形顶点在坐标系内的横纵坐标值。

你只要输出一个实数,即最小方差值 (保留到百分位)。

注意:凸多边形的顶点不一定按顺序给出。

分析

我们先不考虑如何求凸边形的面积如何去求,先考虑如何dp。
这题还是多边形,于是我们考虑将其转换为一些点列,类似的:
f[i,j]表示从第i个点到第j个点之间的面积的最小的方差
(之所以它可以dp是因为可以转换成各个三角形之间的决策,而且最后的答案和sqrt()和/(n-2)运算没有关系)
(但这里注意dp时点的顺序是有关系的,所以后面会写如何求点的顺序)
所以,就可以f[i,j]=min(f[i,j],f[i,k]+f[k,j]+sqr(sum(i,k,j)-ave)),
sum(i,j,k)表示的是边长为i,j,k的长度的三角形的面积。用海伦公式求。
ave指多边形的面积/(n-2),即平均值。
那么现在的问题就是如何求凸多边形的面积。
我们会想到划分成三角形的面积,但是如何确定点的顺序呢?
这里介绍一个函数:
atan2(y,x)的值为坐标为(x,y)与(0,0)的连线与x轴的正半轴的夹角x*pi/180.
那么我们是否可以随便确定一个点(x,y),然后求出其他点(x1,y1)的atan2(y1-y,x1-x)值
排个序后是否就是我们点的顺序,dp的顺序呢?

基本思想是这样的,但是:
1.我们选为基准的点必须是y坐标最小的同时还是相同y坐标最小的x坐标最小。
为什么呢,因为这样就不会出现负数的角度,会方便很多(这个自己推吧)
2.而且当多个点与基准点的y坐标相同时,它们的顺序是按照x坐标排的,即这些点的atan2值为0,的时候。
这样就可以保证不会出错了。

代码

0多边形

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=105,maxlongint=2147483647;
int n,a[N*2],f[N][N],g[N][N],b[N],an[N],d[N];
char ch;
int main(){
    scanf("%d",&n);
    int st=1,en=n,op=0;
    for(int i=1;i<=n;i++){
        scanf("%c",&ch);
        scanf("%c",&ch);
        scanf("%d",&a[i]);
        if (ch=='t') op=1;else op=0;
        b[i]=op;st=i;en=i+1;
    }
    int ans=-maxlongint;
    for(int t=1;t<=n;t++){
    int bz=1;
    for(int i=t;i<=n;i++,bz++) d[bz]=i;
    for(int i=1;i<=t-1;i++,bz++) d[bz]=i;
    memset(f,128,sizeof(f));
    memset(g,127,sizeof(g));
    for(int i=1;i<=n;i++) f[i][i]=g[i][i]=a[d[i]];
    for(int i=1;i<=n-1;i++){
       for(int st=1;st+i<=n;st++){
          for(int j=0;j<i;j++)
            if (b[d[st+j+1]])
            f[st][st+i]=max(f[st][st+i],f[st][st+j]+f[st+j+1][st+i]),
            g[st][st+i]=min(g[st][st+i],g[st][st+j]+g[st+j+1][st+i]);
            else 
            f[st][st+i]=max(max(f[st][st+i],f[st][st+j]*f[st+j+1][st+i]),g[st][st+j]*g[st+j+1][st+i]),
            g[st][st+i]=min(min(g[st][st+i],g[st][st+j]*g[st+j+1][st+i]),g[st][st+j]*f[st+j+1][st+i]);
        }
    }
    an[t]=f[1][n];ans=max(ans,an[t]);
    }
    printf("%d\n",ans);
    for(int i=1;i<=n;i++)if (an[i]==ans) printf("%d ",i);
}

等腰三角形

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int N=55,mo=9397;
int n,m,f[N][N][N];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) f[i][i+2][1]=f[i][i+1][0]=1;
    for(int len=3;len<=n-1;len++){
        for(int i=1;i<=n-len;i++){
            for(int k=1;k<=m;k++){
            if (i==2&&i+len==5&&k==2)
            n=n;
               for(int l=i+1;l<i+len;l++){
                int op=0;
                if (n-(i+len)+i-1==(i+len)-l-1||l-i-1==n-(i+len)+i-1||(i+len-l)==l-i) op=1;
                if (((i+len)==n)&&(i==n-l||n-l==len||l==i)) op=1;
                  for(int r=0;r<=k;r++)
                     f[i][i+len][k]+=f[i][l][r]*f[l][i+len][k-r-op],f[i][i+len][k]%=mo;
                }
            }
        }
    }
    printf("%d",f[1][n][m]);
}

能量项链

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=205;
int n,a[N*2],f[N][N];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i+n]=a[i],a[i+n+n]=a[i];
    n*=2;
    for(int i=1;i<=n;i++){
       for(int st=1;st+i<=n;st++){
          for(int j=0;j<i;j++)
            f[st][st+i]=max(f[st][st+i],f[st][st+j]+f[st+j+1][st+i]+a[st]*a[st+j+1]*a[st+i+1]);
        }
    }
    int ans=0;
    for(int i=1;i<=n/2;i++) 
    ans=max(ans,f[i][i+(n/2)-1]);
    printf("%d",ans);
}

行政划分

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#define db double 
using namespace std;
const int N=55;
int n;
struct xgf{
    db x,y,z;
}a[N];
bool cmp(xgf a,xgf b){
    return (a.z<b.z||(a.z==b.z&&a.x<b.x));
}
db sqr(db x){
    return x*x;
}
db slen(int x,int y){
    return sqrt(sqr(a[x].x-a[y].x)+sqr(a[y].y-a[x].y));
}
db sum(db a,db b,db c){
    db p=(a+b+c)/2*1.0;
    return sqrt(p*(p-a)*(p-b)*(p-c));
}
int main(){
    scanf("%d",&n);
    db mi=2147483647.0;
    db ma=mi;
    int k;
    for(int i=1;i<=n;i++){
        scanf("%lf%lf",&a[i].x,&a[i].y);
        if (mi>a[i].y||mi==a[i].y&&ma>a[i].x) mi=a[i].y,ma=a[i].x,k=i;
    }
    for(int i=1;i<=n;i++) 
    if (i!=k)a[i].z=atan2(a[i].y-a[k].y,a[i].x-a[k].x);
    sort(a+1,a+n+1,cmp);
    db s=0;
    for(int i=3;i<=n;i++){
        s+=sum(slen(1,i),slen(1,i-1),slen(i,i-1));
    }
    s=s/(n-2)*1.0;
    db f[N][N];
    memset(f,127,sizeof(f));
    for(int i=1;i<=n-2;i++) f[i][i+2]=sqr(sum(slen(i,i+1),slen(i,i+2),slen(i+1,i+2))-s);
    for(int i=1;i<=n;i++) f[i][i+1]=0;
    for(int len=1;len<=n-1;len++){
        for(int i=1;i<=n-len;i++){
            for(int j=i+1;j<i+len;j++){
                f[i][i+len]=min(f[i][i+len],f[i][j]+f[j][i+len]+
                sqr(sum(slen(i,j),slen(i,i+len),slen(j,i+len))-s));
            }
        }
    }
    printf("%.2lf",sqrt(f[1][n]/(n-2)*1.0));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值