【UVa】【DP】12105 Bigger is Better

版权声明:本文为lzr原创文章,欢迎大家转载,如需转载请注明:By CQBZ LZR https://blog.csdn.net/qq_37656398/article/details/81841213

UVa 12105 Bigger is Better

题目

◇题目传送门◆(由于UVa较慢,这里提供一份vjudge的链接)
◇题目传送门(vjudge)◆

题目大意

使用不超过 N 根的火柴摆出一个尽量大的、能够被 M 整除的正整数。数字的摆放方法如下图所示:

思路

一道极其明显的数位DP。。。

相信大家都看了紫书的两种做法(但是我根本没有看懂)。但是我在这里将给出一个极其简洁的做法。

所以我们可以定义状态 f [ i ] [ j ] 为使用不超过 i 根火柴,拼出来的数模 M j 的数的最长长度。

因为在火柴根数相等的情况下,拼出来的数越长,数就越大。

则极易列出状态转移方程:

f [ i ] [ j ] = m a x { f [ i c ( d ) ] [ ( j × 10 + d ) mod M ] + 1 }

其中 d 为当前往数的末尾中添加的一位数字, c ( d ) 表示拼出数字 d 需要的火柴数量。

因为要输出方案,所以计算时记录一下路径,逆序输出即可。

是不是很简洁?:-)

正解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int Maxn=100;
const int Maxm=3000;
const int ned[]={6,2,5,5,4,5,6,3,7,6};

int N,M;
int f[Maxn+5][Maxm+5];
int p[Maxn+5][Maxm+5];

int main() {
    #ifdef LOACL
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    int cas=0;
    while(scanf("%d %d",&N,&M)==2) {
        memset(f,-1,sizeof f);
        memset(p,-1,sizeof p);
        for(int i=0;i<=N;i++)
            for(int j=0;j<M;j++) {
                if(j==0)f[i][j]=0;
                for(int d=9;d>=0;d--)//注意逆序枚举!!!
                    if(i>=ned[d]) {
                        int t=f[i-ned[d]][(j*10+d)%M];
                        if(t>=0&&t+1>f[i][j]) {//只有当状态合法时才转移
                            f[i][j]=t+1;
                            p[i][j]=d;
                        }//记录当前所选中的数字d
                    }
            }
        printf("Case %d: ",++cas);
        if(p[N][0]<0) {
            printf("-1");
        } else {
            int i=N,j=0;//逆序输出
            for(int d=p[i][j];d>=0;d=p[i][j]) {
                printf("%d",d);
                i-=ned[d];
                j=(j*10+d)%M;
            }
        }
        puts("");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37656398/article/details/81841213