smoj2249 数学家 (DP套贪心)
【题目描述】
【解题思路】
先证明一个结论:
设北墙的草坪i所包含的南墙草坪si块。
不会存在si-sj>1(i≠j,且si>sj)
这样是不会出现图1的情况的,因为转化成图2会更优。
那么只需把原来的褐色部分的换成黄色部分,也就是把分界线移到中间。
因为
x+y=T
x2+y2=(x+y)2−2xy=T2−2xy
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[i−1][k]+(j−k)2∗k1+g[j−k][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