您的位置:首页 > 其它

例题9-22 越大越好 UVa12105

2015-10-08 14:49 435 查看
1.题目描述:点击打开链接

2.解题思路:本题是数位dp题目。根据题意,可以定义状态(i,j)表示用不超过i根火柴拼出“除以m余数为j”的整数。设dp(i,j)表示满足状态(i,j)的整数的最大长度,p(i,j)表示满足状态(i,j)的整数的最高位的数字。那么可以列出下面的状态转移方程:

dp(i,j)=max{dp(i-c[x],(j*10+x)%m)+1}(c[x]<=i);

p(i,j)=x;

上述方程表示当我们从高位往低位写数字的时候,假设当前写的数字是x,高位的数字除以m的余数为j,那么把x添加到前面写过的数字后面时候,相当于先对前一次写出来的数*10,然后加上x,那么这个新数的余数就是(j*10+x)%m。由于dp值表示的位数,因此我们取位数最大的那个dp值,然后+1,就是状态(i,j)的最大长度,同时更新状态(i,j)对应的那个数字x,又因为我们希望写出的数尽可能的大,因此枚举x的h时候要从大到小枚举,而且必须满足c[x]<=i,其中c[x]表示写出数字x需要使用的火柴数。

上述方程可以令i由小到大递推计算,这样,最后的答案只需要从p
[0]开始往回走即可得到最后的答案。

3.代码:

#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<list>
#include<complex>
#include<functional>
using namespace std;

#define me(s) memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
typedef long long ll;
typedef pair <int,int> P;

const int maxn=100+5;
const int maxm=3000+5;

int n,m;
int dp[maxn][maxm];
int p[maxn][maxm];

int needs[]={6,2,5,5,4,5,6,3,7,6};

int main()
{
int kase=0;
while(~scanf("%d",&n)&&n)
{
scanf("%d",&m);
printf("Case %d: ",++kase);

for(int i=0;i<=n;i++)
for(int j=0;j<m;j++)
{
int&ans=dp[i][j];
ans=p[i][j]=-1;//首先初始化为-1
if(!j)ans=0; //注意:如果余数为0,那么初始化为0
for(int d=9;d>=0;d--)
if(i>=needs[d])
{
int t=dp[i-needs[d]][(j*10+d)%m];
if(t>=0&&t+1>ans)
{
ans=t+1;
p[i][j]=d;
}
}
}
if(p
[0]<0)puts("-1"); //如果用n根火柴不能组成余数为0的数字,那么无解
else
{
int i=n,j=0;//利用p数组来从火柴数多的情况到火柴数少的情况走
for(int d=p[i][j];d>=0;d=p[i][j])
{
printf("%d",d);
i-=needs[d]; //更新火柴数
j=(j*10+d)%m; //更新余数
}
puts("");
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数位dp