专题1:记忆化搜索/DAG问题/基础动态规划

    A OpenJ_Bailian 1088 滑雪
    B OpenJ_Bailian 1579 Function Run Fun
    C HDU 1078 FatMouse and Cheese
    D POJ 3280 Cheapest Palindrome
    E OpenJ_Bailian 1976 A Mini Locomotive
    F OpenJ_Bailian 2111 Millenium Leapcow
    G OpenJ_Bailian 1141 Brackets Sequence
    H HDU 2848 Number Cutting Game
    I OpenJ_Bailian 1191 棋盘分割

 ------------------------

A poj 1088

经典水题,裸记忆化搜索

题意:在一个矩阵上,每一个格子有自己的高度,只能从高格子向周围4方向低格子移动,求最长路

分析:

  2种思路

  1.  进行剪枝,朴素的标记当前位置的最长路,只有当更优时,才进行dfs

       实际上,这种算法很容易超时(时限1000ms)

      

 1 /**********************
 2 *@Name:A - OpenJ_Bailian - 1088 
 3 *
 4 *@Author: Nervending
 5 *@Describtion:
 6 *@DateTime: 2018-01-22 15:19:01
 7 ***********************/
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 const int maxn=1e2+10;
11 const int INF=0x3f3f3f3f;
12 int len[maxn][maxn];
13 int g[maxn][maxn];
14 int n,m;
15 int ans;
16 const int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
17 void dfs(int nx,int ny,int step){
18     if(step<=len[nx][ny]) return;
19      len[nx][ny]=step;
20     for(int k=0;k<4;k++){
21         int x=nx+dir[k][0];
22         int y=ny+dir[k][1];
23         if(g[x][y]<g[nx][ny]&&len[x][y]<step+1){
24             dfs(x,y,step+1);
25         }
26     }
27 }
28 
29 int main(){
30 //    freopen("in.txt","r",stdin);
31 //    freopen("out.txt","w",stdout);
32     
33     while(~scanf("%d%d",&n,&m)){
34         int sx,sy;
35         int mx=-INF;
36         for(int i=1;i<=n;i++){
37             for(int j=1;j<=m;j++){
38                 scanf("%d",&g[i][j]);
39             }
40         }
41         memset(len,0,sizeof len);
42         for(int i=1;i<=n;i++){
43             for(int j=1;j<=m;j++){
44                     dfs(i,j,1);
45             }
46         }
47         int ans=-1;
48         for(int i=1;i<=n;i++){
49             for(int j=1;j<=m;j++){
50                 ans=max(ans,len[i][j]);
51             }
52         }
53         printf("%d\n",ans);
54     }
55     return 0;
56 }
View Code

 

  2.  转化为DAG最长路问题,记忆化搜索并标记,关键点在于每个点只需要访问1次,时间快了200倍,空间小了4倍

      

 1 /**********************
 2 *@Name:A - OpenJ_Bailian - 1088 
 3 *
 4 *@Author: Nervending
 5 *@Describtion:
 6 *@DateTime: 2018-01-22 15:19:01
 7 ***********************/
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 const int maxn=1e2+10;
11 const int INF=0x3f3f3f3f;
12 int len[maxn][maxn];
13 int g[maxn][maxn];
14 int n,m;
15 int ans;
16 const int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
17 int dfs(int nx,int ny){
18     if(len[nx][ny]) return len[nx][ny];
19     int mx=1;
20     for(int k=0;k<4;k++){
21         int x=nx+dir[k][0];
22         int y=ny+dir[k][1];
23         if(g[x][y]<g[nx][ny]){
24             mx=max(dfs(x,y)+1,mx);
25         }
26     }
27     return len[nx][ny]=mx;
28 }
29 
30 int main(){
31 //    freopen("in.txt","r",stdin);
32 //    freopen("out.txt","w",stdout);
33     while(~scanf("%d%d",&n,&m)){
34         memset(g,0x3f,sizeof g);
35         memset(len,0,sizeof len);
36         for(int i=1;i<=n;i++){
37             for(int j=1;j<=m;j++){
38                 scanf("%d",&g[i][j]);
39             }
40         }
41         int ans=0; 
42         for(int i=1;i<=n;i++){
43             for(int j=1;j<=m;j++){
44                     len[i][j]=dfs(i,j);
45                     ans=max(len[i][j],ans);
46             }
47         }
48         printf("%d\n",ans);
49     }
50     return 0;
51 }
View Code

  ------------------------

B  poj 1579

经典水题,裸记忆化搜索

题意:给出一个递归函数的伪代码:

  function w(a, b, c):
       if a <=0 or b <=0 or c <=0, then returns:1
       if a >20or b >20or c >20, then returns: w(20,20,20)
       if a < b and b < c, then returns: w(a, b, c-1)+ w(a, b-1, c-1)- w(a, b-1, c)    
       otherwise it returns: w(a-1, b, c)+ w(a-1, b-1, c)+ w(a-1, b, c-1)
现在输入abc 求w(a,b,c);
分析:
    直接递归调用会超时,复杂度为指数级别,进行记忆化,本质上还是个DAG,每个点只需要访问1次
    因此时间复杂度O(n^3) n==20
      
 1 /**********************
 2 *@Name:
 3 *
 4 *@Author: Nervending
 5 *@Describtion:
 6 *@DateTime: 2018-01-22 22:51:02
 7 ***********************/
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 const int maxn=1e2+10;
11 const int INF=0x3f3f3f3f;
12 long long dp[maxn][maxn][maxn];
13 int a,b,c;
14 long long dfs(int a,int b,int c){
15     if(a<=0||b<=0||c<=0) return 1;
16     if(a>20||b>20||c>20) return dfs(20,20,20);
17     if(dp[a][b][c])    return dp[a][b][c];
18     if(a<b&&b<c) return dp[a][b][c]=dfs(a,b,c-1)+dfs(a,b-1,c-1)-dfs(a,b-1,c);
19     else return dp[a][b][c]=dfs(a-1,b,c)+dfs(a-1,b-1,c)+dfs(a-1,b,c-1)-dfs(a-1,b-1,c-1);
20 }
21 
22 int main(){
23 //    freopen("in.txt","r",stdin);
24 //    freopen("out.txt","w",stdout);
25     memset(dp,0,sizeof dp);
26     while(cin>>a>>b>>c){
27         if(a==-1&&b==-1&&c==-1){
28             break;
29         }
30         long long ans=dfs(a,b,c);
31         printf("w(%d, %d, %d) = %lld\n",a,b,c,ans);
32     }
33 
34 
35     return 0;
36 }
View Code

 ------------------------

 C hdu1078

经典水题,裸记忆化搜索,注意读题

题意:一个n*n的正数矩阵,从0,0点出发,每次可以向四个方向跳跃1-k步,要求落点的值大于起点的值,并获得当前点的值,

  求权值最大的路

分析:

    依然是DAG的记忆化搜索,每个点只访问一次即可,复杂度O(n^2) n==1e2

      

    我写这道题时,一开始没有仔细看题,忽略了起点固定+只能直线(是否停下获取当前权值还是继续走),即使是记忆化搜索,复杂度也飙升到O(n^4*k) n==1e2,k==1e2错了很多发

 1 /**********************
 2 *@Name:
 3 *
 4 *@Author: Nervending
 5 *@Describtion:
 6 *@DateTime: 2018-01-22 23:03:07
 7 ***********************/
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 const int maxn=1e2+5;
11 const int INF=0x3f3f3f3f;
12 const int dir[4][2]={1,0,-1,0,0,1,0,-1};
13 int g[maxn][maxn];
14 int dp[maxn][maxn];
15 #define dx dir[d][1]
16 #define dy dir[d][0]
17 #define check(x,y) (x>=1&&y>=1&&x<=n&&y<=n)
18 int n,k;
19 int dfs(int nx,int ny){
20     if(dp[nx][ny]) return dp[nx][ny];
21     int len=g[nx][ny];
22     for(int i=1;i<=k;i++){
23         for(int d=0;d<4;d++){
24             int x=nx+dx*i;
25             int y=ny+dy*i;
26             if(check(x,y)&&g[x][y]>g[nx][ny])
27                 len=max(len,dfs(x,y)+g[nx][ny]);
28         }
29     }
30     return dp[nx][ny]=len;
31 }
32 int main(){
33 //    freopen("in.txt","r",stdin);
34 //    freopen("out.txt","w",stdout);
35     while(cin>>n>>k){
36         if(n==-1&&k==-1) break;
37         k=min(n,k);
38         memset(g,0,sizeof g);
39         memset(dp,0,sizeof dp);
40         for(int i=1;i<=n;i++){
41             for(int j=1;j<=n;j++){
42                 cin>>g[i][j];
43             }
44         }
45         int ans=dfs(1,1);
46         cout<<ans<<endl;
47     }
48 
49     return 0;
50 }
View Code

 ------------------------

D poj 3280

记忆化搜索/裸的区间DP

题意:给定一个小写字母字符串,可以在字符串任何地方插入和删除字母,每种字母有修改和删除的花费

   求让字符串变成回文串的最小花费

分析:

  其实是裸的区间DP,但题目时限比较松(2000ms),也可以用DAG上的记忆化搜索解决

  对于dp[a][b]表示把a-b变为回文串的最小花费,不断选择是删除/添加Sa还是删除/添加Sb

    

  需要注意的是,在前面删除Sa和在后面添加Sa,结果是一样的,也就是a被匹配了,所以只需要保存删除Sa和添加Sa中较小的一个就行

 1 /**********************
 2 *@Name:
 3 *
 4 *@Author: Nervending
 5 *@Describtion:
 6 *@DateTime: 2018-01-23 01:40:04
 7 ***********************/
 8 #include <iostream>
 9 #include <cstdio>
10 #include <algorithm>
11 #include <string.h>
12 using namespace std;
13 const int maxn=2e3+100;
14 const int INF=0x3f3f3f3f;
15 int n,m;
16 char id[maxn];
17 int cost[maxn];
18 int dp[maxn][maxn];
19 int dfs(int a,int b){
20     if(dp[a][b]!=-1) return dp[a][b];
21     if(a==b) return dp[a][b]=0;
22     int c=(int)id[a];
23     int d=(int)id[b];
24     int ans;
25     if(c==d){
26         ans=dfs(a+1,b-1);
27     }else {
28         ans=dfs(a+1,b)+cost[c];
29         ans=min(dfs(a,b-1)+cost[d],ans);
30     }
31     return dp[a][b]=ans;
32 }
33 int main(){
34 //    freopen("in.txt","r",stdin);
35 //    freopen("out.txt","w",stdout);
36     while(~scanf("%d%d%s",&n,&m,id)){
37         memset(dp,-1,sizeof dp);
38         for(int i=0;i<n;i++){
39             char ch[10];
40             int a,b;
41             scanf("%s%d%d",&ch,&a,&b);;
42             cost[(int)ch[0]]=min(a,b);
43         }
44         printf("%d\n",dfs(0,m-1));
45     }
46     return 0;
47 }
View Code

  若使用区间DP的非递归写法,则可以大幅度优化.

 ------------------------

E poj 1976

裸01背包

题意:

  给一个长为n的数列,选出至多3个连续不相交的字段,要求每个字段的长度不超过m

  求选出的字段的最大和

分析:

  裸01背包

  转化为DAG上的DFS(记忆化搜索)似乎...过不去?,一直是TLE/WA

 
 1 /**********************
 2 *@Name:
 3 *
 4 *@Author: Nervending
 5 *@Describtion:
 6 *@DateTime: 2018-01-23 03:50:55
 7 ***********************/
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 const int maxn=2e5+10;
11 const int INF=0x3f3f3f3f;
12 int num[maxn];
13 int sum[maxn];
14 int dp[maxn][4];
15 int n,m;
16 int dfs(int car,int numh){
17     if(car>=3) return 0; 
18     if(dp[car][numh]) return dp[car][numh];
19     int ans=0;
20     for(int i=numh;i<=n;i++){
21         ans=max(dfs(car+1,i+m)+sum[i+m]-sum[i],ans);
22     }
23     return dp[car][numh]=ans;
24 }
25 int main(){
26 //    freopen("in.txt","r",stdin);
27 //    freopen("out.txt","w",stdout);
28     int casn;
29     cin>>casn;
30     while(casn--){
31         cin>>n;
32         memset(num,0,sizeof num);
33         memset(dp,0,sizeof dp);
34         memset(sum,0,sizeof sum);
35         for(int i=1;i<=n;i++){
36             scanf("%d",&num[i]); 
37         }
38         for(int i=1;i<=maxn;i++){
39             sum[i]=sum[i-1]+num[i];
40         }
41         cin>>m;
42         cout<<dfs(0,0)<<endl;
43     }
44     return 0;
45 }
View Code

  改成01背包,即可AC

  dp[i][j]表示i个车头在前j个车中最多拉多少乘客,遍历即可

    

 1 /**********************
 2 *@Name:
 3 *
 4 *@Author: Nervending
 5 *@Describtion:
 6 *@DateTime: 2018-01-23 03:50:55
 7 ***********************/
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 const int maxn=2e5+10;
11 const int INF=0x3f3f3f3f;
12 int num[maxn];
13 int sum[maxn];
14 int dp[maxn][4];
15 int n,m;
16 int main(){
17 ////    freopen("in.txt","r",stdin);
18 //    freopen("out.txt","w",stdout);
19     int casn;
20     cin>>casn;
21     while(casn--){
22         cin>>n;
23         memset(num,0,sizeof num);
24         memset(dp,0,sizeof dp);
25         memset(sum,0,sizeof sum);
26         for(int i=1;i<=n;i++){
27             scanf("%d",&num[i]); 
28         }
29         for(int i=1;i<=maxn;i++){
30             sum[i]=sum[i-1]+num[i];
31         }
32         cin>>m;
33         for(int i=1;i<=3;i++){
34             for(int j=m;j<=n;j++){
35                 int t=sum[j]-sum[j-m];
36                 dp[j][i]=max(dp[j-1][i],dp[j-m][i-1]+t);
37             }
38         }
39         cout<<dp[n][3]<<endl;
40     }
41     return 0;
42 }
View Code

 ------------------------

 F poj 2111

裸记忆化搜索

题意;

  给一个n*n矩阵,从任一点开始,有一个马,可以向周围"日"走一步,要求输出最长路并还原路径,多解输出字典序最小的一个

分析:

  和A题基本一样,不过移动方式改为象棋中马的走法,并增加要求,输出字典序最小的路径,具体做法和最短路的路径还原是一样的,保留前驱节点即可

    

 1 /**********************
 2 *@Name:
 3 *
 4 *@Author: Nervending
 5 *@Describtion:
 6 *@DateTime: 2018-01-23 12:42:26
 7 ***********************/
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 const int maxn=5e2+10;
11 const int INF=0x3f3f3f3f;
12 const int dir[8][2]={2,1,2,-1,-2,1,-2,-1,1,2,-1,2,1,-2,-1,-2};
13 #define dx dir[d][0]
14 #define dy dir[d][1]
15 #define check(x,y) (x>=1&&y>=1&&x<=n&&y<=n)
16 int pre[maxn][maxn][2];
17 int g[maxn][maxn];
18 int dp[maxn][maxn];
19 int n;
20 int dfs(int nx,int  ny){
21     if(dp[nx][ny]) return dp[nx][ny];
22     int ans=0;
23     int tx=0,ty=0;
24     for(int d=0;d<8;d++){
25         int x=nx+dx;
26         int y=ny+dy;
27         if(check(x,y)&&g[x][y]>g[nx][ny]){
28             int t=dfs(x,y);
29             if(ans<t||ans==t&&g[x][y]<g[tx][ty]){
30                 tx=x;
31                 ty=y;
32                 ans=t;
33             }
34         }
35     }
36     if(ans){
37         pre[nx][ny][0]=tx;
38         pre[nx][ny][1]=ty;
39     }
40     return dp[nx][ny]=ans+1;
41 }
42 
43 int main(){
44 //    freopen("in.txt","r",stdin);
45 //    freopen("out.txt","w",stdout);
46     while(~scanf("%d",&n)){
47         memset(g,0x3f,sizeof g);
48         memset(dp,0,sizeof dp);
49         memset(pre,-1,sizeof pre);
50         for(int i=1;i<=n;i++){
51             for(int j=1;j<=n;j++){
52                 scanf("%d",&g[i][j]);
53             }
54         }
55         int ans=0;
56         int sx=1,sy=1;
57         for(int i=1;i<=n;i++){
58             for(int j=1;j<=n;j++){
59                 int t=dfs(i,j);
60                 if(ans<t||ans==t&&g[sx][sy]>g[i][j]){
61                     ans=t;
62                     sx=i,sy=j;
63                 }
64             }
65         }
66         printf("%d\n",ans);
67         while(sx!=-1){
68             printf("%d\n",g[sx][sy]);
69             int t=sx;
70             sx=pre[sx][sy][0];
71             sy=pre[t][sy][1];
72         }
73     }
74     return 0;
75 }
View Code

 ------------------------ 

G poj 1141

裸的区间dp,难点在于路径还原

题意:

   给一个[]()组成的字符串,添加最少的[]()使其匹配,并输出一个最小解答

分析:

  首先,如何获得最小花费?

  首先如果一个区间的两端可以匹配,则可以转移到[l+1,r-1]这个问题

  其次,无论是否匹配,都可以划分为2个子问题 [l][k]和[k+1][r]上

  枚举k和外侧匹配进行比较,就可以从之前子问题的解答得到当前问题的最优解了

  这时候,其实就可以写出记忆化搜索的代码了

  但我们考虑线性的dp过程,

  显然,最终答案是从子结构得到的,很容易发现,当前答案一定是从更小的区间得到的

  因此我们把DAG分为多层,按区间长度分层

  然后就可以很流畅的想到尺取法,递增的枚举区间的长度,并将窗口平移,对窗内进行状态转移即可

  其次,如何获得最小解答的字符串

  首先 如果对于一个子串,如果是两侧配对最优,

    我们可以先输出左括号,再输出[l+1][r-1],再输出右括号

  其次,如果两侧匹配的对象是新添加的一个括号,

    我们可以在状态转移的时候用一个pos[l][r]保存最优解时区间添加的那个括号是在哪里

    打印[l,pos[l][r],pos[l][r]+1,r]

     

  递归的终点就是长度为1的时候,直接输出一对括号即可  

 1 /**********************
 2 *@Name:
 3 *
 4 *@Author: Nervending
 5 *@Describtion:
 6 *@DateTime: 2018-01-23 18:49:11
 7 ***********************/
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 const int maxn=1e3+10;
11 const int INF=0x3f3f3f3f;
12 #define check(x,y) (s[x]=='('&&s[y]==')'||s[x]=='['&&s[y]==']')
13 int n;
14 char s[maxn];
15 int dp[maxn][maxn],pos[maxn][maxn];
16 void output(int a,int b){
17     if(a>b) return ;
18     if(a==b){
19         if(s[a]=='('||s[a]==')') printf("()");
20         else printf("[]");
21     }else{
22         if(pos[a][b]==-1){
23             putchar(s[a]);
24             output(a+1,b-1);
25             putchar(s[b]);
26         }else {
27             output(a,pos[a][b]);
28             output(pos[a][b]+1,b);
29         }
30     }
31 }
32 
33 int main(){
34 //    freopen("in.txt","r",stdin);
35 //    freopen("out.txt","w",stdout);
36     while(gets(s)){
37         n=strlen(s);
38         memset(dp,0,sizeof dp);
39         for(int i=1;i<n;i++){
40             for(int j=0,k=i;k<n;j++,k++){
41                 if(check(j,k)){
42                     dp[j][k]=dp[j+1][k-1]+2;
43                     pos[j][k]=-1;
44                 }
45                 for(int p=j;p<k;p++){
46                     if(dp[j][p]+dp[p+1][k]>=dp[j][k]){
47                         dp[j][k]=dp[j][p]+dp[p+1][k];
48                         pos[j][k]=p;
49                     }
50                 }
51             }
52         }
53         output(0,n-1);
54     }
55 
56     return 0;
57 }
View Code

 ------------------------

H hdu 2848

博弈转DAG

其实并不是很需要动态规划或者记忆化搜索,是一个DAG状态图上的DFS

这道题是09年多校的一道题,并不出名,提交记录和网络上的题解也很少

我事后查了查,貌似只有代码流传...那个代码也很迷,写的很拖沓

题意:

  给一个n和k k<=logn 此处log以10为底,两个人轮流操作,把数字n分为k个段,

  每段为xi,然后n=sigma(xi) 并继续操作

  如果无法分割为k段,判定为失败,给n和k,问先手的人是否有必胜策略

分析:

  大概思路如下,定义dfs(a,b,c) a为还没分割的段落,b为已经分割的段落和,c为段落数量,

  初始状态就是dfs(n,0,1),

  假设当前为dfs(a,b,c)则可以状态转移到dfs(a/10,b+a%10,2) dfs(a/100,b+a%100,2)....

  当c==k时 转移到!dfs(a+b,0,1) 直到结束

     

  这个时间还可以,最大时限是1000ms,如果要优化,可以记忆化,哈希一下即可,但是因为可能的状态空间极大,遍历的重复几率却很低,所以不是很必要

 1 /**********************
 2 *@Name:
 3 *
 4 *@Author: Nervending
 5 *@Describtion:
 6 *@DateTime: 2018-01-24 17:21:28
 7 ***********************/
 8 #include <cstdio>
 9 #include <cstring>
10 #include <algorithm>
11 #include <iostream>
12 using namespace std;
13 const int maxn=1e3+10;
14 const int INF=0x3f3f3f3f;
15 #define ll long long
16 ll cmp[maxn];
17 ll n;
18 int k;
19 bool dfs(ll a,ll b,int now){
20     if(now==1&&a<cmp[k-1]) return false;
21     if(now==k)return !dfs(a+b,0,1);
22     for(int i=1;i<20;i++){
23         if(a<cmp[i]) break;
24         if(dfs(a/cmp[i],b+a%cmp[i],now+1))return true;
25     }
26     return false;
27 }
28 
29 int main(){
30 //    freopen("in.txt","r",stdin);
31 //    freopen("out.txt","w",stdout);
32 
33 
34     cmp[0]=1;
35     for(int i=1;i<=19;i++){
36             cmp[i]=cmp[i-1]*10;
37     }
38     while(~scanf("%lld%d",&n,&k)){
39         printf("%d\n",dfs(n,0,1));
40     }
41     return 0;
42 }
View Code

 ------------------------

I poj 1191

刘汝佳,算法艺术与信息学竞赛(黑书)p116页的例题,99年noi的一道题

很经典的一个区间dp

中文题+经典题,题意不多赘述,可以直接点上面的题号链接

分析:

  经典题,就不分析记忆化搜索的DAG形态再转化到线性dp了

  我们先不用题目中的方程,考虑另外一个均方差的方程 E=(sigma(xi)^2)/n-avg^2

  可以发现,最优解和sum(i,j,a,b)相关的

  可以预处理所有sum(1,1,a,b) 其他子块的可以用简单的容斥定理得到

  然后就是dp

  怎么从记忆化的DAG搜索转化为线性DP就不在多说,先枚举次数,4重循环枚举块,再枚举横着切/竖着切的位置,即可得到答案

    

  时间复杂度还是很满意的

 1 /**********************
 2 *@Name:
 3 *
 4 *@Author: Nervending
 5 *@Describtion:
 6 *@DateTime: 2018-01-23 21:56:19
 7 ***********************/
 8 #include <bits/stdc++.h>
 9 #define rep(x,a,b) for(int x=a;x<b;x++)
10 using namespace std;
11 const int maxn=10;
12 const int INF=0x3f3f3f3f;
13 int g[maxn][maxn];
14 int n;
15 int sum[maxn][maxn];
16 double avg;
17 int dp[maxn<<1][maxn][maxn][maxn][maxn];
18 
19 int main(){
20 //    freopen("in.txt","r",stdin);
21 //    freopen("out.txt","w",stdout);
22     while(~scanf("%d",&n)){
23         memset(sum,0,sizeof sum);
24         avg=0;
25         for(int i=1;i<=8;i++){
26             for(int j=1;j<=8;j++){
27                 scanf("%d",&g[i][j]);
28                 sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+g[i][j];
29                 avg+=g[i][j];
30             }
31         }
32         avg=avg/(double)n;
33         rep(i,1,9)
34         rep(j,1,9)
35         rep(k,i,9)
36         rep(p,j,9){
37             int s=sum[k][p]-sum[i-1][p]-sum[k][j-1]+sum[i-1][j-1];
38             dp[0][i][j][k][p]=s*s;
39         }
40         for(int d=1;d<n;d++){
41             rep(i,1,9)
42             rep(j,1,9)
43             rep(k,i,9)
44             rep(p,j,9){
45                 int t=INF;
46                 rep(a,i,k){
47                     t=min(dp[d-1][i][j][a][p]+dp[0][a+1][j][k][p],t);
48                     t=min(dp[0][i][j][a][p]+dp[d-1][a+1][j][k][p],t);
49                 }
50                 rep(b,j,p){
51                     t=min(dp[d-1][i][j][k][b]+dp[0][i][b+1][k][p],t);
52                     t=min(dp[0][i][j][k][b]+dp[d-1][i][b+1][k][p],t);
53                 }
54                 dp[d][i][j][k][p]=t;
55             }
56         }
57         double ans=dp[n-1][1][1][8][8];
58         ans=sqrt(ans/n-avg*avg);
59         printf("%.3lf\n",ans);
60     }
61     return 0;
62 }
View Code

有问题欢迎留言

posted @ 2018-01-22 16:47  nervending  阅读(375)  评论(0编辑  收藏  举报