标签:
给一个 n∗m 的矩阵,矩阵的每个格子上有一个不超过 30 的非负整数。 我们定义一条合法的路线是从(1,1)开始只能向右和向下移动到达(n,m)的路线。 定义数列 A1,A2,A3,..,An+m−1 为一条合法路线上的序列,且 Aavg 为该数列的平均值。该路线的 价值为 (n + m−1) 乘上该数列的方差。 即价值的表达式为 (n + m−1)∑n+m−1 i=1 (Ai−Aavg)2。 请找一条价值最小的路线,并输出这个价值。
对于 100% 的数据,n,m,ai,j ≤ 30
我们发现对于一条确定的路径sigma((a[i]-s)^2)是一个关于s的二次函数,而方差是二次函数的最低点
在数据大的时候会有很多的二次函数,这些二次函数最低点的最小的那个就是答案
这些二次函数的最小值大概会形成一个类似单谷函数的东西。
于是三分。
这是错误的,但是很难卡掉。
小数据暴力
时间复杂度 O(n^2logn)
实际上是标程一个牺牲一定准确性的优化
#include<stdio.h> #include<iostream> #include<stdlib.h> #include<math.h> #include<algorithm> #include<string> #include<string.h> #include<set> #include<map> #include<vector> #define il inline #define re register using namespace std; typedef double db; int n,m,a[101][101],dir[101],b[101]; db val[101][101],f[101][101]; il db sqr(db x){return x*x;} il void small(){ db final=1e50; for(int S=(1<<(n+m-2))-1;S>=0;--S){ for(int i=0;i<n+m-2;i++) dir[i]=((S&(1<<i))>0); int u=1,v=1,flag=true; db ans,cnt=a[1][1]; for(int i=0;i<n+m-2;i++){ if(dir[i]) u++; else v++; if(!(1<=u&&u<=n&&1<=v&&v<=m)){ flag=false;break; } b[i]=a[u][v];cnt+=b[i]; } if(!flag) continue; cnt=cnt/(n+m-1);ans=sqr(cnt-a[1][1]); for(int i=0;i<n+m-2;i++){ ans+=sqr(cnt-b[i]); } final=min(final,ans*(n+m-1)); } printf("%.0lf",final); } il db res(db ave){ for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) val[i][j]=sqr(ave-a[i][j]); f[1][1]=val[1][1]; for(int i=2;i<=m;i++) f[1][i]=f[1][i-1]+val[1][i]; for(int i=2;i<=n;i++) f[i][1]=f[i-1][1]+val[i][1]; for(int i=2;i<=n;i++){ for(int j=2;j<=m;j++) f[i][j]=min(f[i-1][j],f[i][j-1])+val[i][j]; } return f[n][m]*(n+m-1); } int main(){ freopen("route.in","r",stdin); freopen("route.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]); if(n+m<=20){ small();exit(0); } db l=0,r=30,m1,m2; for(int i=1;i<=100;i++){ m1=(r-l)/3+l; m2=r-(r-l)/3; // cout<<l<<" "<<m1<<" "<<m2<<" "<<r<<endl; if(res(m1)<res(m2)) r=m2; else l=m1; } printf("%.0lf",res((l+r)/2)); return 0; }
标签:
原文地址:http://www.cnblogs.com/ExiledPoet/p/5771126.html