1. 程式人生 > >小奇的矩陣(動態規劃

小奇的矩陣(動態規劃

【題目 背景】

小奇總是在數學課上思考奇怪的問題。

【問題描述】

給定一個 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('\n')
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 }