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

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

         

你可能感兴趣的:(c,ACM,回溯,记忆化搜索,递推)