幸运数字
题目描述:
小红最近迷上了幸运数字。他所认为的幸运数字是指只由4或7组成的数字比如44, 7774, 4就是幸运数字, 而5,17, 4437等就不是。现在他想知道一个最小的幸运数字, 使得这个数字的各个位数字之和等于n。
输入描述:
第一行输入一个数字, n;n大于等于1, 小于等于1000000 代表小红想要的数字之和。
输出描述:
输出最小的那个幸运数字, 如果不存在, 那么输出-1。
题解:
首先这道题我们可以写一个扩展欧几里得,因为数据范围还是比较小,就设4的个数为x,7的个数为y,4x + 7y = n,我们需要使x + y的值尽可能小,使7的个数尽可能多。
但下面我们来写一种dp写法,我们定义一个结构体变量dp[ i ],其中包含的元素有cnt:数的位数,val:4的个数加上七的个数乘上一个常数1000000 / 4 ,还需要一个数 to[ i ]表示当前是在更新4 or 7,还需要一个e数组表示当前val要加上多少;
dp[ i ]数组的下标表示数的各位数字之和,然后我们就可以得到dp状态转移方程:
w.cnt = dp[ i - to[ j ] ] + 1;
w.val = dp[ i - to[ j ] ] + e[ j ];
dp[ i ] = min ( dp[ i ] , w );
这里的min()需要自己重新定义结构体比较,返回位数小的,若位数一样,返回7更少的,即val更大的。
具体解释见代码注释:
#include<cstdio> #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N = 1e6 + 1; struct node{ int cnt,val; }dp[N]; int n, to[2] = {4,7}, e[2] = {1,1000000 / 4};//e[]的用处是将4的个数和7的个数都巧妙地存起来,存在val中 node min(node a, node b){ if (a.cnt == b.cnt) return a.val < b.val ? a : b;//如上文 return a.cnt < b.cnt ? a : b; } void spfa(){ memset(dp, 0x3f, sizeof(dp)); dp[0].cnt = dp[0].val = 0; for (int i = 1; i <= 100000; i++) for (int j = 0; j < 2; j++){ if (i - to[j] < 0) continue; node w; w.cnt = dp[i - to[j]].cnt + 1; w.val = dp[i - to[j]].val + e[j];//每次j = 0就加上1,而j = 1的时候就加上1000000/4
这样val/(100000/4)就是7的个数,而val%(100000/4)
就是4的个数 dp[i] = min(dp[i], w); } } int main(){ spfa();//先处理处1~1000000的值,预处理,然后查询 scanf("%d", &n); if (dp[n].cnt > e[1]){//表示求出的位数超过了最大位数,就说明找不到 printf("-1"); return 0; } int four = dp[n].val % e[1]; int seven = dp[n].val / e[1]; for (int i = 1; i <= four; i++) printf("4"); for (int i = 1; i <= seven; i++) printf("7"); return 0; }