例题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("");
}
}
}
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("");
}
}
}
相关文章推荐
- acdream 1064
- hdu 2089 不要62 数位dp
- 关于矩阵优化的DP总结
- HDU 2089不要62 数位dp
- nbut 1545 New Year 2014 数位dp
- BZOJ3029 花神的数论题
- 数位DP
- HDU 2089 不要62
- 数位DP/数位统计 初探
- BZOJ1833: [ZJOI2010]count 数字计数
- 动态规划10_数位DP2
- 动态规划10_数位DP1
- 【打CF,学算法——三星级】Codeforces 9C Hexadecimal's Numbers (解法汇总)
- 动态规划晋级——HDU 3555 Bomb【数位DP详解】
- URAL 1057 数位DP
- hdu 2451 Simple Addition Expression(数位dp)
- 数位dp 入门 nbut 1475
- UVA 1640(数位统计)
- poj 1655Balancing Act(找重心,树形dp)
- HYSBZ 1026 windy数 (数位DP)