关于smoj2249数学家的另类解法

smoj2249 数学家 (DP套贪心)

【题目描述】
【解题思路】

  先证明一个结论:
  设北墙的草坪i所包含的南墙草坪si块。
  不会存在si-sj>1(i≠j,且si>sj)
这里写图片描述
  这样是不会出现图1的情况的,因为转化成图2会更优。
  那么只需把原来的褐色部分的换成黄色部分,也就是把分界线移到中间。
  因为
   x+y=T
   x2+y2=(x+y)22xy=T22xy
   xy<(x+y)24
  所以x与y接近的时候,xy取的值会比较大,也就是面积会变小。
  因此,将s数组求出来,即s[i]=n/m,剩下的n%m平摊。
  例如:m=6 n=15
  s1=2,s2=2,s3=2,s4=3,s5=3,s6=3。
  然后再跑一次DP即可。
  g[i][j]表示长度为i,用j块南草坪去铺,所用的最小面积。
  F[i][j]表示到了第i块北草坪,长度为j。
  g数组直接贪心求。(具体看代码)
   F[i][j]=F[i1][k]+(jk)2k1+g[jk][si]
  程序跑得飞快……

【代码】
#include<cstdio>
#include<algorithm>

#define imax(a,b) ((a>b)?(a):(b))
#define imin(a,b) ((a<b)?(a):(b))

using namespace std;

typedef long long ll;

const double oo=1e9;
const double eps=1e-8;
const int N=120;
double f1,f2;
int n,m,lu[N],g[N][N];
double ans,f[2][N];

double getku(int len,int x)
{
    double kks=len*len*f1+g[len][x]*f2;
    return kks;
}

int main()
{
    freopen("2249.in","r",stdin);
    freopen("2249.out","w",stdout);
    scanf("%lf%lf%d%d",&f1,&f2,&n,&m);
    for(int i=1;i<=100;i++)
    {
        g[i][0]=1e9;
        g[i][1]=i*i;
        for(int j=2;j<=i;j++)
        {
            int yu=i/j;
            g[i][j]=g[i-yu][j-1]+yu*yu;
        }
    }
    int ys=m/n,yw=m%n;
    for(int i=1;i<=n;i++) lu[i]=ys;
    for(int i=1;i<=yw;i++) lu[i]++;
    for(int i=0;i<=100;i++) f[1][i]=getku(i,lu[1]);
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=100;j++)
        {
            int now=i&1,ls=now^1;
            f[now][j]=oo;
            for(int h=1;h<=j-i+1;h++)
            {
                double asd=f[ls][j-h]+getku(h,lu[i]);
                if(asd<f[now][j]) f[now][j]=asd; else break;
            }
        }
    }
    printf("%.1lf\n",f[n&1][100]);
    return 0;
}

PS:还有更优的解法。
  其实这道题在2005年noi国家集训队的论文里有详细的解法及证明,我就不一一写了。
PPT传送门:从《小H的小屋》的解法谈算法的优化
提取码:spyg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值