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 1073741824 int 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<<30 int 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<<30 int 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了,大胆的看吧(*^__^*) 。