圈乘运算问题
问题描述
关于十进制整数的2元圈乘运算@定义如下:十进制数x,y,x@y=sum_x*max_y+min_y,sum_x等于x的各位数字之和,max_y为y的各位数字中的最大数字,min_y为y的各位数字中的最小数字。例如19@30=10*3+0=30。给定十进制整数X和K,由X、圈乘运算符@和小括号可以组成多种表达式,如X@X,X@X@X,X@(X@X),规定@运算符是左结合的。
试设计一个算法,计算出由X 、@和小括号;运算组成的值为K 的表达式最少需用多少个 @ 运算。
数据输入:从标准输入,输入X和K
数据输出:从标准输出,输出最少的运算次数
输入示例:3 12
输出示例:1
解题思路:此题比较明显能够看出适合动态规划算法求解。但是难点在于如何分析最优子结构和重叠子问题,以及递推模型。
要解决此问题,需要知道圈乘运算的一个性质:闭包性质(这个名字是我自己随意起的~不一定符合数学规范~)
对于任意的正整数X,Y,若X,Y<=81*N+9(N>=2),则X@Y<=81*N+9
证明如下:
令X=a1a2..am,Y=b1b2..bn,X@Y=(a1+a2+..+am)*max{b1,b2,..bn}+min{b1,b2,..bn}<=(a1+a2+..+am)*9+9
要证明X@Y<=81*N+9,只需证明(a1+a2+..+am)<=9*N
首先当N=2时,81*N+9=171,显然,任何小于171的整数中各位数字之和最大的数位99,所以a1+a2<=9*2,结论成立
当N>=3时,81*N+9是一个位数小于等于N的整数,因此,X,Y的位数小于等于N,所以(a1+a2+..+an)<=9*N,
综上,(a1+a2+..+an)<=9*N,所以X@Y<=81*N+9
有了此性质,此题就比较好解决了。设m=X的位数,则有X、@和括号组成的表达式的值小于等于81*max{2,m}+9
设K=Mi@Nj,f(K)表示表达式值为K的最小运算次数,则f(K)=min{f(Mi)+f(Nj)},i=1..m,j=1..n。
代码如下:
#include<stdio.h>
#include<malloc.h>
#define MAX 100000000
int length(int x)
{
int tmp=x;
int len=0;
while(tmp>0)
{
len++;
tmp/=10;
}
return len;
}
void factor(int *array,int n)
{
int min=10;
int max=0;
int sum=0;
while(n>0)
{
int t1=n%10;
//int t2=n/10;
n/=10;
sum+=t1;
if(t1>max)
max=t1;
if(t1<min)
min=t1;
}
array[0]=MAX;
array[1]=sum;
array[2]=min;
array[3]=max;
}
int min_circle_operation(int x,int k)
{
int max_res=0;
int m=length(x);
int max=81*m+9;
if(max<171)
max=171;
if(max<k)
{
return -1;
}
int **s=(int **)malloc(sizeof(int *)*(max+2));
int i=0;
for(i=0;i<=max;i++)
{
/*s[i][0]保存的是得到i的表达式需要的最少运算次数,s[i][1]保存的是i的各位数字之和,s[i][2]表示的是i的最小数字,s[i][3]表示的是i的最大数字*/
s[i]=(int *)malloc(sizeof(int)*4);
factor(s[i],i);
}
s[max+1]=(int *)malloc(sizeof(int)*4);
factor(s[max+1],x);
s[max+1][0]=0;
printf("hello\n");
int flag=1;
int j=0;
/*设立flag标志,若s中的值,随着循环的进行不再更新则跳出循环返回结果*/
while(flag)
{
flag=0;
/**让i和j进行@运算,直到1~max各位数字的最小运算次数均找到*/
for(i=1;i<=max+1;i++)
{
if(s[i][0]<MAX)
{
for(j=1;j<=max+1;j++)
{
if(s[j][0]<MAX)
{
int tmp=s[i][1]*s[j][3]+s[j][2];
if(tmp>max_res)
max_res=tmp;
if(s[tmp][0]>s[i][0]+s[j][0]+1)
{
s[tmp][0]=s[i][0]+s[j][0]+1;
flag=1;
}
}
}//for
}//if
}//for
}//while
if(s[k][0]<MAX)
return s[k][0];
return -1;
}
int main(int argc,char *argv[])
{
int x,k;
scanf("%d%d",&x,&k);
int result=min_circle_operation(x,k);
if(result==-1)
printf("no answer\n");
else printf("result is %d\n",result);
}