uvaoj384最优矩阵链乘(详解 + 思考 + 记忆化搜索 + 递推 )

来源:互联网 发布:足下软件学院校长 编辑:程序博客网 时间:2024/05/29 05:55

uvaoj384


       本题一直让我WA的是  INF的设定,一定要大! 2^30  (1073741824) 

            输出格式要留心一些就可以啦,注意只输入一组答案是什么  

            比如:

            1

            5 1


           输出是  (A1)  不是 A1

        

printf("Case %d: ",count++);if(n == 1){printf("(A1)\n");continue;}   //单独输出dfs( 0 ,n);print(0 , n);printf("\n");



在给几组测试的数据     


10
2 84
84 66
66 8
8 410
410 8
8 96
96 10
10 200
200 10
10 2
10
2 64
64 8
8 55
55 333
333 44
44 887
887 554
554 1
1 226
226 33
0


output

Case 1: (((A1 x A2) x A3) x (A4 x (A5 x (A6 x (A7 x (A8 x (A9 x A10)))))))
Case 2: ((A1 x (A2 x (A3 x (A4 x (A5 x (A6 x (A7 x A8))))))) x (A9 x A10))

记忆化搜索好想,注意好区间的控制就可以,要知道每一层的回退,都会得到一个区间内的链乘最小值,记录到

d[][]中,一定要理解d所存值的意义。

#include<stdio.h>#include<string.h>#define min(a,b) a<b?a:b#define INF 1073741824int d[20][20], a[20],vis[20][20] ,sum = 0;  int dfs(int l ,int h){if(h-l==1)return 0;if(d[l][h]>0)return d[l][h];int k,ans;ans =  INF;for(k = l+1;k<h;k++){if(ans > dfs(l,k) + dfs(k,h) + a[l]*a[k]*a[h]){ans = d[l][k] + d[k][h] + a[l]*a[k]*a[h];  //回退后的区间对应有最小值,已经存在d[][]了vis[l][h] = k;}}d[l][h]=ans;return ans;  }void print(int l ,int h){   if(h-l==1){ printf("A%d",l+1);return;}   int k = vis[l][h];      printf("(");      print(l,k);   printf(" x ");   print(k,h);   printf(")");   }int main(){int n,i,j,count =1;while(scanf("%d",&n)){    memset(d,0,sizeof(d));memset(vis,0,sizeof(vis));if(n==0)break;for(i = 0;i<n;i++){scanf("%d %d",&a[i],&a[i+1]);}printf("Case %d: ",count++);if(n == 1){printf("(A1)\n");continue;}  //单独写出来dfs( 0 ,n);print(0 , n);printf("\n");}return 0;}


数组递推

    最有意思的就是二维数组了,它可以表示任意连续区间 i到 j (i表示该区间开始的位置,j表示该区间结尾的位置。一共占整个二维数组空间的上三角位置。

    前提,先理解 二维数组 在这里的意义。

    递推思路:

    1.通过区间的大小开始递推,从长度为1 的区间开始推(从小区间推大区间),然后推到n 。这时, d表示区间大小的变量决定一层循环

    2.区间的递推  需要有开始的位置,这时,表示行的 i  就表示开始的位置(上三角),决定一层循环

    3. i的位置,d的大小,决定了 j(区间结束的位置)

    4. k的枚举分割了,了 i 到 j 的区间。 dp[i][j] = min( dp[i][j] , d[i][k] + d[k+1][j] + w(i,j,k)) (每层的分割都建立在上一层分割的基础上,而上一层的分割已经确定了最小链乘值。



    理解之后,写递推就方便多了。

    以单个矩阵为基础的区间划分  (左闭右闭)

#include<stdio.h>#define INF 1<<30int dp[20][20],path[20][20];void print(int l ,int h)   {   if(h == l){ printf("A%d",l+1);return;}    //不知道大家注意到没,这里的条件变了,由于这是以每一个矩阵为基础去划分区间   int k = path[l][h];              //上个记忆化搜索是以  把矩阵的每个元素x,y打到了一位数组上了,那么就是以元素为基础去划分区间   printf("(");      print(l,k);   printf(" x ");   print(k+1,h);                      //注意   printf(")");}int main(){int n,i,j,k,d,ans,count = 1;int left[20],right[20];while(scanf("%d",&n)){if(n ==0)break;for(i =0;i<n ;i++){scanf("%d %d",&left[i],&right[i]);}for(i = 0;i<=n;i++)dp[i][i] = 0;for(i =0;i<=n;i++)for(j =i+1;j<=n;j++)          dp[i][j] = INF;for(d = 1;d<n ;d++)for( i =0;i+d <n;i++){j = i+d;                               //最少两个for( k =i;k<j;k++){if(dp[i][j]>dp[i][k] + dp[k+1][j] + left[i]*right[k]*right[j]){dp[i][j] = dp[i][k] + dp[k+1][j] + left[i]*right[k]*right[j];path[i][j] = k;}}}  printf("Case %d: ",count++);if(n == 1){printf("(A1)\n");continue;}print(0 , n-1);                //注意printf("\n");}return 0;}

    在此,再给出 将元素打到一维数组上,以元素为基础的区间划分,输出函数又和写记忆化搜索是一样的了

(左闭右开)


#include<stdio.h>#include<string.h>#define INF 1<<30int dp[20][20],path[20][20];void print(int l ,int h){   if(h - l == 1){ printf("A%d",l+1);return;}   int k = path[l][h];      printf("(");      print(l,k);   printf(" x ");   print(k,h);                   //注意   printf(")");   }int main(){int n,i,j,k,d,ans,count = 1;int v[20];while(scanf("%d",&n)){if(n ==0)break;for(i =0;i<n ;i++){scanf("%d %d",&v[i],&v[i+1]);}memset(dp,0,sizeof(dp));for(d = 1;d<= n ;d++)for( i =0;i+d<=n;i++){j = i+d+1;                      //最少三个元素dp[i][j] = INF;for( k =i+1;k<j;k++){if(dp[i][j]>dp[i][k] + dp[k][j] + v[i]*v[k]*v[j]){dp[i][j] = dp[i][k] + dp[k][j] + v[i]*v[k]*v[j];path[i][j] = k;}}}  printf("Case %d: ",count++);if(n == 1){printf("(A1)\n");continue;}print(0 , n);             //注意printf("\n");}return 0;}



以上代码,全部AC了,大胆的看吧(*^__^*) 

         

1 0
原创粉丝点击