zoukankan      html  css  js  c++  java
  • 专题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
    ",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
    ",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
    ",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
    ",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
    ",ans);
    67         while(sx!=-1){
    68             printf("%d
    ",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
    ",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
    ",ans);
    60     }
    61     return 0;
    62 }
    View Code

    有问题欢迎留言

  • 相关阅读:
    并发编程(2)-进程、并发和并行讲解
    并发编程(5)-管道、数据共享、进程池
    并发编程(4)-进程中的锁、信号量、 事件和队列
    人工智能及数学运算的基础方法
    并发编程(3)-进程模块
    判断一个数是否是水仙花数
    js中隐式类型转换测试
    webpack使用webpack-dev-middleware进行热重载
    网页打包安卓APP流程
    「postgres」查看数据库连接数
  • 原文地址:https://www.cnblogs.com/nervendnig/p/8329840.html
Copyright © 2011-2022 走看看