洛谷U611532 bigger is better

20 阅读1分钟

upd:250920 注:紫书中所说的第三种方法有问题,弃用。原judge-uva12105没有考虑前导0问题,已更新自己的数据到洛谷题目上。

www.luogu.com.cn/problem/U61…

以下是紫书中的第二种方法。

dp[i][j]表示拼了i位数,余数是j时,需要的最少火柴棒数量。

获得答案的时候,从高位到低位,每一位从9到0遍历,尽量挑大的使用,注意答案不能有前导0,例如0003,0000,可以是0。

#include<iostream>
#include<cstdio>
using namespace std;

#define inf 105

const int maxn=105,maxm=3005;
int dp[maxn][maxm],r[maxn][10];
int n,m,kase;
int need[15]={6,2,5,5,4,5,6,3,7,6};

int main()
{
	//freopen("expin.txt","r",stdin);
	//freopen("expout3.txt","w",stdout);
	
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	while(cin>>n>>m && n)
	{
		cout<<"Case "<<++kase<<": ";
		//pre-compute r array
		for(int i=1;i<=9;i++) r[1][i]=i%m;
		for(int i=2;i<=n;i++)
		{
			for(int d=1;d<=9;d++)
			{
				r[i][d]=(r[i-1][d]*(10%m))%m;
			}
		}
		
		//initialize dp array
		for(int i=0;i<=n;i++) {
			for(int j=0;j<m;j++) dp[i][j]=inf;
		}
		dp[0][0]=0;
		
		for(int i=0;i<=n;i++){
			for(int j=0;j<m;j++){
				for(int d=9;d>=0;d--){
					int &ans=dp[i+1][(j*10+d)%m];
					ans=min(ans,dp[i][j]+need[d]);
				}
			}
		}
		
		int i;
		for(i=n;i>0;i--) if(dp[i][0]<=n) break;
		
		if(i==0) cout<<-1<<"\n";
		else{
			int t=0;
			bool leading_zero=1;
			for(;i>=1;i--){
				int d;
				for(d=9;d>=0;d--)
				{
					int nxt=(t-r[i][d]+m)%m;
					if(dp[i-1][nxt]+need[d]<=n)
					{
						n-=need[d];
						t=nxt;
						break;
					}
				}
				if(i>1 && d==0 && leading_zero==1) continue;
				leading_zero=0;
				cout<<d;
			}
			if(leading_zero==1) cout<<-1;
			cout<<"\n";
		}
	}
	
	return 0;
}