1 条题解
-
0
自动搬运
来自洛谷,原作者为

2018李泽明
这个家伙很弱,什么也没有留下搬运于
2025-08-24 21:28:56,当前版本为作者最后更新于2019-08-04 21:52:07,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
前言
这道题是真的恶心,我肝了一个晚上……
这是P1773 符文之语_NOI导刊2010提高(02)的题解。一道****(无法用语言形容)的DP题,今早考试里被它虐的死去活来(我好蒟啊)。这道题从第一眼看到它我就知道是DP,等到讲题前都没能设出状态……
我们以乘号划分阶段,即f[i][j]表示到第i个数字,满足最小乘积(对m取余后的)所需要划分的阶段(要加的括号)。sum[i][j]表示从第i个数字到第j个数字的乘积对m取余。
DP可以从小到大推,也可以从大到小推,看各位习惯。这里只提供从小到大推的程序(个人习惯)
在程序中的所有乘积后都要记得对m取余(非常重要)
Code
#include<cstdio> #include<cstring> using namespace std; int f[1010][60],sum[1010][1010],m,lon; char zfc[1010]; int main() { memset(f,0x7F,sizeof(f));//因为我是从前到后推的,所以我一开始把f数组定义成无穷大 scanf("%s\n%d",zfc,&m);//先输入字符串,不要忘记换行 lon=strlen(zfc);//lon记录字符串的长度 for(int i=1;i<=lon;i++)//sum[i][i]表示从字符串的第i个到第i个的乘积 sum[i][i]=zfc[i-1]-'0';//当前这一位到自己只有一位啊,所以取不取余倒也没有什么关系 for(int i=lon;i>=1;i--)//i是从后往前查的 for(int j=i+1;j<=lon;j++)//j从前往后查,所以f[i][j-1]显然是已经知道了的 sum[i][j]=(sum[i][j-1]*10%m+sum[j][j])%m;//从i到j显然是i到j-1位的乘积乘10加上当前这一位 for(int i=1;i<=lon;i++)//f[i][sum[1][i]]表示从第1个点到第i个点时乘积为sum[1][i]所要加的乘号 f[i][sum[1][i]]=0;//仔细想一想,一个乘号都不用加,乘积为sum[1][i]的数就是字符串前i位的乘积 for(int i=1;i<=lon;i++)//枚举字符串 for(int j=1;j<i;j++)//从j到i的区间 for(int k=0;k<m;k++)//乘积取余,求的是答案 if(f[j][k]+1<f[i][k*sum[j+1][i]%m])//如果在第j个数字后放乘号能比原来的决策好 f[i][k*sum[j+1][i]%m]=f[j][k]+1;//更新 for(int i=0;i<m;i++)//从小到大推肯定要从前面找答案啊,如果不是0x7F说明这个状态存在 if(f[lon][i]<0x7F) { printf("%d %d ",i,f[lon][i]);//不输出等啥呢? break; } for(int i=m;i>=0;i--)//最大值同理啊 if(f[lon][i]<0x7F) { printf("%d %d",i,f[lon][i]); break; } return 0; }这道题主要是状态难设,其他倒也没有什么。所以大家要努力攻克DP啊。
方程不规范,爆零两行泪……
- 1
信息
- ID
- 746
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者