圈乘运算问题

圈乘运算问题

问题描述

关于十进制整数的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);
}





评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值