【NOIP模拟赛】小奇的矩阵
【题目背景】
小奇总是在数学课上思考奇怪的问题。
【问题描述】
给定一个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
题解
又有一道DP题没有做出来。
首先我们可以把式子拆开可以拆成sum1~n+m-1(Ai^2)-sum1~n+m-1(Ai),那么就可以用设dp[S][i][j]表示处于(i,j)和为S的最小sum1~n+m-1(Ai^2),转移就是向下,向右走就是了。
注意,不合法的状态不能让他更新其他状态。
代码:
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <iostream> #define N (n+m)*30 #define MAXN 110 #define ll long long using namespace std; int n,m; ll dp[2000][35][35],v[110][110]; void DP(){ dp[v[1][1]][1][1]=v[1][1]*v[1][1]; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=0;k<=N;k++) if(dp[k][i][j]!=dp[0][0][0]){ int x=i+1,y=j,s=k+v[x][y]; dp[s][x][y]=min(dp[s][x][y],dp[k][i][j]+v[x][y]*v[x][y]); x=i,y=j+1,s=k+v[x][y]; dp[s][x][y]=min(dp[s][x][y],dp[k][i][j]+v[x][y]*v[x][y]); } } int main() { int t;cin>>t; while(t--){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)scanf("%lld",&v[i][j]); memset(dp,127,sizeof(dp)); DP(); ll ans=dp[0][0][0]; for(int s=0;s<=N;s++){ if(dp[s][n][m]!=dp[0][0][0]){ ans=min(ans,(n+m-1)*dp[s][n][m]-s*s); } } printf("%lld\n",ans); } return 0; }