zoukankan      html  css  js  c++  java
  • 小奇的矩阵(动态规划

    【题目 背景】

    小奇总是在数学课上思考奇怪的问题。

    【问题描述】

    给定一个 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, 描述这个矩阵。

    【输出格式】

    对于每次询问, 输出一行一个整数表示要求的结果

    【样例输入】

    12 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


    下面全都是自己的话

    又是一道小奇系列的题,感觉要被可爱的小奇洗脑了(麻烦你作为一只小猫不要再数学课上胡思乱想好不好啦)

    首先一看这个神秘的式子,初中学历以上的OIer们在内心尖叫:我知道!这是在求方差!

    可是为什么要求平均数而最后的结果还是整数啊??是不是把小数点全抹掉四舍五入啊??会不会爆精度啊??NOIP好像不让用long double 来着...

    别急,我们先看数据规模,并不是很大,熟悉动态规划的同学应该都会直接想到DP了。但是——我要走到终点才能知道平均数是多少啊,这个后效性阻断了了我的AC之路。。

    所以还是回到式子上,考虑一下化简。

    观察到式子的一开始乘了一个(n+m-1),化简后居然把所有所有的分母都消去了(!)

    最终化简出来大概是这样

    (n+m-1)*(a1^2 + a2^2 + a3^2 +...+ai^2)-(a1+a2+a3+...+ai)^2

    啊哈,原来根本不用什么double型嘛,结果都是整数的。

    那我们接下来考虑DP方程怎么写吧,要怎么表示每一个坐标的状态呢??
    ......
    ......
    ......
    ......
    ......
    f[i][j][.....??
    ......
    ......

    同样的Sigma(ai)是可以表示不同的Sigma(ai^2)的(!)

    所以我们的数组f[i][j][k],前两维就表示走到的位置(i,j)咯,然后k表示此时的Sigma(ai)(就是刚刚那个式子后面的部分除去平方),这个状态下式子的前半部分(n+m-1)*(a1^2 + a2^2 + a3^2 +...+ai^2) 最小是多少(!)太好了这样就可以转移了,既满足最优子结构的性质,又满足无后效性。

    相信大家都能自己写出转移方程了吧(实在不行就看代码吧)

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<algorithm>
     6 
     7 #define For(i,a,b) for(register int i=a;i<=b;++i)
     8 #define Dwn(i,a,b) for(register int i=a;i>=b;--i)
     9 #define Re register
    10 #define Pn putchar('
    ')
    11 #define llg long long
    12 using namespace std;
    13 const int N=32;
    14 int n,m,a[N][N],nxm,Mx=0;
    15 llg f[N][N][2000];
    16 inline void read(int &v){
    17     v=0;
    18     char c=getchar();
    19     while(c<'0'||c>'9')c=getchar();
    20     while(c>='0'&&c<='9')v=v*10+c-'0',c=getchar();
    21 }
    22 void write(llg x){
    23     if(x>9)write(x/10);
    24     int xx=x%10;
    25     putchar(xx+'0');
    26 }
    27 int main(){
    28     freopen("matrix.in","r",stdin);
    29     freopen("matrix.out","w",stdout);
    30     int T; read(T);
    31     while(T--){
    32         read(n); read(m);
    33         nxm=n+m-1;
    34         For(i,1,n) For(j,1,m)read(a[i][j]);
    35         
    36         memset(f,-1,sizeof(f));
    37         
    38         f[1][1][a[1][1]]=nxm*a[1][1]*a[1][1];
    39         Mx=a[1][1];
    40         
    41         For(i,1,n) For(j,1,m){
    42             if(i==1&&j==1)continue;
    43             int adx=a[i][j];
    44             int pMx=Mx;
    45             Dwn(k,pMx,1){
    46                 llg fx;
    47                 
    48                 fx=f[i-1][j][k];   // I walk to you from your top side
    49                 if(fx!=-1){
    50                     Mx=max(Mx,k+adx);
    51                 
    52                     if(f[i][j][k+adx]==-1){
    53                         f[i][j][k+adx]=fx+nxm*adx*adx;
    54                     }else{
    55                         f[i][j][k+adx]=min(f[i][j][k+adx],fx+nxm*adx*adx);
    56                     }
    57                 }
    58                 
    59                 fx=f[i][j-1][k];  // I walk to you from your left side
    60                 if(fx!=-1){
    61                     Mx=max(Mx,k+adx);
    62                     
    63                     if(f[i][j][k+adx]==-1){
    64                         f[i][j][k+adx]=fx+nxm*adx*adx;
    65                     }else{
    66                         f[i][j][k+adx]=min(f[i][j][k+adx],fx+nxm*adx*adx);
    67                     }
    68                 }
    69                 
    70             }
    71             
    72         }
    73 
    74         llg ans=1e18;
    75         For(i,1,Mx) if(f[n][m][i]!=-1){
    76             ans=min(ans,f[n][m][i]-i*i);
    77         }
    78         write(ans); Pn;
    79     }
    80     fclose(stdin); fclose(stdout);
    81     return 0;
    82 }

     希望能得到支持(推荐) 

     

  • 相关阅读:
    荷兰国旗问题
    读取文件中数据到数组
    从五个球中选出3个枚举的简单运用
    搜索算法总结
    匿名对象
    欧几里得距离C++代码实现
    用递归法吧字符串S倒序
    利用系统来完成十进制,十六进制,八进制的转换
    DBHelper 使用的是存储过程
    DBHelper的一个小例子
  • 原文地址:https://www.cnblogs.com/HLAUV/p/9893323.html
Copyright © 2011-2022 走看看