1. 程式人生 > >12105 Bigger is Better ( DP )

12105 Bigger is Better ( DP )

題意:

給出n個木棒,擺出能被m整除的最大的數

分析:

有一種非常顯而易見的dp方法: 
f[i][j]表示用i根火柴拼出的“%m是j”的最大整數 
轉移方程:

f[i+c[k]][(j*10+k)%m]=max{f[i][j]+k}
時間複雜度是O(10*nm),看上去好像非常優秀 
但是這樣的狀態值是高精度(能拼成的數可能很大),因此實際計算量非常大 
(再說有人願意隨手寫一個高精度嗎。。。)

下面就說一個有點難度,但是效率很高的演算法: 
f[i][j]表示拼出“%m是j的i位數”所用的最少火柴 
轉移方程:

f[i+1][(j*10+k)%m]=min{f[i][j]+c[k]}
那麼我們怎麼根據f陣列確定解呢 
首先我們先確定下來最高位(在轉移的時候順便維護一下就好了) 
之後我們從高位向低位列舉每一位的數字 
舉個簡單的例子:m=7,並且已經確定最高位是3 
首先試著讓最高位為9,如果我們能夠擺出9xx這樣的合法數字,那麼ta一定是最大的 
是否可以擺出9xx呢? 
因為900%7=4,因此後兩位%7的餘數應該是3 
如果d[2][3]+c[9]<=n,那麼最高位就可以是9

重複上述過程,直到所有數字都被確定 
在計算過程中,我們需要快速算出形如 x000… 的整數%m的答案,這需要我們一開始預處理一下

tip
d<—INF,d[0][0]=0

注意
我們計算出來的f只是輔助構造解的 
我們在構造解的時候運用貪心的思想,讓位數儘量多,每一位儘量大 
所以只要符合

d[now-1][(limit-c[k]+m)%m]+c[k]<=n

#include<bits/stdc++.h>

using namespace std;

int n,m;        //n個木棒,能被m整除
int cnt[]={6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
int dp[105][3005];  //dp[i][j]表示i位數字,除以m餘數為j最少要的火柴數
int mod[10][105];   //mod[i][j]表示數字i,後面有j為位0,組成的數字模m的值

int main(){
    int cs=1;
    while(~scanf("%d",&n),n){
        scanf("%d",&m);
        printf("Case %d: ",cs++);
        memset(dp,0x3f,sizeof dp);
        dp[0][0]=0;
        for(int i=0;i<=9;i++){
            dp[1][i%m]=min(dp[1][i%m],cnt[i]);
        }
        for(int i=2;i<=n/2;i++){        //最多n/2位  全1
            for(int j=0;j<m;j++){
                for(int k=0;k<=9;k++){
                    dp[i][(j*10+k)%m]=min(dp[i][(j*10+k)%m],dp[i-1][j]+cnt[k]);
                }
            }
        }
        int ans=0;
        for(int i=n/2;i>=0;i--){
            if(dp[i][0]<=n){
                ans=i;break;
            }
        }
        if(ans==0){
            printf("-1\n");
            continue;
        }
        memset(mod,0,sizeof mod);
        for(int i=0;i<=9;i++){
            mod[i][0]=i%m;
            for(int j=1;j<=100;j++){
                mod[i][j]=mod[i][j-1]*10%m;
            }
        }
        int tmp=0;
        while(ans>0){
            for(int i=9;i>=0;i--){
                if(dp[ans-1][(m+tmp-mod[i][ans-1])%m]<=n-cnt[i]){
                    printf("%d",i);
                    n-=cnt[i];
                    tmp=(m+tmp-mod[i][ans-1])%m;
                    break;
                }
            }
            ans--;
        }
        puts("");
    }
}