「NOIP模拟赛」[hdu5492] 小奇的矩阵
「题目背景」
小奇总是在数学课上思考奇怪的问题。
「问题描述」
给定一个n*m的矩阵,矩阵中的每个元素aij为正整数。
接下来规定
1.合法的路径初始从矩阵左上角出发,每次只能向右或向下走,终点为右下角。
2.路径经过的n+m-1个格子中的元素为A1,A2…A(n+m-1),Aavg为Ai的平均数,路径的V值为(n+m-1)*∑(Ai-Aavg) ^2
(1<=i<=n+m-1)
求V值最小的合法路径,输出V值即可,有多组测试数据。
「输入格式」
第一行包含一个正整数T,表示数据组数。
对于每组数据:
第一行包含两个正整数n和m,表示矩阵的行数和列数。
接下来n行,每行m个正整数aij,描述这个矩阵。
「输出格式」
对于每次询问,输出一行一个整数表示要求的结果
「样例输入」
1
2 2
1 2
3 4
「样例输出」
14
「数据范围」
对于30%的数据 n<=10,m<=10
有另外40%的数据 n<=15 m<=15,矩阵中的元素不大于5
对于100%的数据 T<=5,n<=30,m<=30,矩阵中的元素不大于30
题解
对于30%的数据,把所有的路径深搜出来取最优,复杂度是T*C(2n,n)*n
这道题乍一看像是dp,但是问题在于因为求的是方差与平均数有关,每一步的代价似乎不能直接得出
对于另外40%的数据,我们发现路径Ai和SN不超过120,考虑枚举SN,平均值为av=SN/N(N=n+m-1),那我们就能得到每一步的代价,但是要保证求的路径符合Ai和为SN,设计f(s,i,j)表示到(i,j),和为s的最小代价
f(s,i,j)=min{f(s-a[i][j],i,j-1),f(s-a[i][j],i-1,j)}+(av-a[i][j])^2
复杂度是T*SN^2*n^2
事实上,路径Ai和不等于SN也无所谓,因为对于一条路径,只有我们枚举的SN等于路径Ai和时,这条路径的方差才最小
设我们枚举的SN比真实的Ai和ε,ε是任意实数
很容易证明∑(Ai-av)^2<∑(Ai-av+ε)^2(全部展开)
所以直接去掉dp的第一维,复杂度是T*SN*n^2,可以通过100%的数据
还有另外一种思路,把求方差的式子展开
得到N*∑(Ai^2)-SN^2,设计f(s,i,j)表示到(i,j),和为s的最小的N*∑(Ai^2),最后枚举s取f(s,i,j)-s^2的最小值,复杂度也是T*SN*n^2
深搜
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#include<map> #include<set> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #define inf 1000000000 #define ll long long using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int ans; int T,n,m; int a[35][35],b[105]; void cal() { int sum=0,tot=0; for(int i=1;i<=n+m-1;i++) sum+=b[i],tot+=b[i]*b[i]; ans=min(ans,tot*(n+m-1)-sum*sum); } void dfs(int x,int y,int k) { b[k]=a[x][y]; if(x==n&&y==m) { cal(); return; } if(x+1<=n)dfs(x+1,y,k+1); if(y+1<=m)dfs(x,y+1,k+1); } int main() { freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout); T=read(); for(int cas=1;cas<=T;cas++) { ans=inf; n=read();m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read(); dfs(1,1,1); printf("%d\n",ans); } return 0; } |
正解1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#include<map> #include<set> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #define inf 1000000000 #define ll long long #define N 1800 using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } double ans; int T,n,m; int a[35][35]; double f[35][35]; double sqr(double x) { return x*x; } void dp(int x) { for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) f[i][j]=1000000; double av=(double)x/(n+m-1); f[1][1]=sqr(a[1][1]-av); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(f[i][j]!=1000000) { int x=i+1,y=j,v=a[x][y]; f[x][y]=min(f[x][y],f[i][j]+sqr(v-av)); x=i,y=j+1,v=a[x][y]; f[x][y]=min(f[x][y],f[i][j]+sqr(v-av)); } ans=min(ans,f[n][m]); } int main() { freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout); T=read(); for(int cas=1;cas<=T;cas++) { //printf("Case #%d: ",cas); ans=inf; n=read();m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read(); for(int i=1;i<=N;i++)dp(i); printf("%d\n",(int)(ans*(n+m-1)+1e-5)); } return 0; } |
正解2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
#include<map> #include<set> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #define inf 1000000000 #define ll long long #define N (n+m)*30 using namespace std; ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int ans; int T,n,m; int a[35][35]; int f[1805][35][35]; void dp() { f[a[1][1]][1][1]=a[1][1]*a[1][1]; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=0;k<=N;k++) if(f[k][i][j]!=1000000) { int x=i+1,y=j,v=a[x][y]; f[k+v][x][y]=min(f[k+v][x][y],f[k][i][j]+v*v); x=i,y=j+1,v=a[x][y]; f[k+v][x][y]=min(f[k+v][x][y],f[k][i][j]+v*v); } for(int i=1;i<=N;i++) if(f[i][n][m]!=1000000) ans=min(ans,(n+m-1)*f[i][n][m]-i*i); } int main() { freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout); T=read(); for(int cas=1;cas<=T;cas++) { //printf("Case #%d: ",cas); ans=inf; n=read();m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=0;k<=N;k++) f[k][i][j]=1000000; dp(); printf("%d\n",ans); } return 0; } |
求发NOIP2015游记!
我并没有参加
天辣!
多谢回答!
黄学长,请问
设我们枚举的SN比真实的Ai和小E,E是任意实数
很容易证明∑(Ai-Aavg)^2<∑(Ai-av+E)^2
怎么证明?
全部展开,消完一边会剩下NE^2