UVALive 4310 Minimal Multiple
2016-01-31 10:03
274 查看
题目地址:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&category=329&page=show_problem&problem=2311
题目大意:将给你的数重新排序 要求一个数位原位置与新位置的距离不能超过K,问可以有多少个数是M的倍数(对10007取余),同时求出最小的数。(新数不可以有前导0)
思路:首先考虑 dp[i][j][k] 表示:正在处理第i位,前缀对M取模的余数为j,k是一个二进制数表示周围K个数是否已被用过的数字个数。
我们先从特殊情况入手,我们假设读入为 n=12345678 K=1。
![](https://img-blog.csdn.net/20160131154204276?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
(上图为初始化状态)
其中 k对应的二进制数就是011(2)=3(10)
二进制中1的表示对应数位还没有被占用,同时距离当前数位的距离不超过K,接下来我们考虑转移。
(一) 当前行占用k二进制中的第一个1的位置
![](https://img-blog.csdn.net/20160131154310310?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
则转移为
![](https://img-blog.csdn.net/20160131154336498?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
(二) 当前行占用k二进制中的第二个1的位置
![](https://img-blog.csdn.net/20160131154411043?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
则转移为
![](https://img-blog.csdn.net/20160131154432965?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
从特殊情况中,已经看出了大致的转移方法,尝试放入k二进制中1的位置,再将整体右移处理下一位。不过在本题中还要考虑不能有前导0的情况需要进行判断,同时需要指出的是如果k的二进制中首位是1,则转移只有一种,把首位的1放入,不然之后会出现不够放的情况。
我们在考虑一种特殊情况n=3333 m=1 k=1
答案很明显是1 3333。但是如果只考虑了上述情况的转移就会出现重复的情况答案会是3
3333。接下来考虑如何避免重复。事实上,我们从高位到低位枚举的时候只枚举没有出现过的数字这样就不会重复了。
![](https://img-blog.csdn.net/20160131154501546?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
转移一:
![](https://img-blog.csdn.net/20160131154529231?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](https://img-blog.csdn.net/20160131154550700?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
转移二:
![](https://img-blog.csdn.net/20160131154612981?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
因为已经有过对应数位为3的转移,所以这种转移非法要忽视掉。
最后要提醒一点:dp出来的值为0 不一定是0还可能是10007的倍数需要仔细处理。
题目大意:将给你的数重新排序 要求一个数位原位置与新位置的距离不能超过K,问可以有多少个数是M的倍数(对10007取余),同时求出最小的数。(新数不可以有前导0)
思路:首先考虑 dp[i][j][k] 表示:正在处理第i位,前缀对M取模的余数为j,k是一个二进制数表示周围K个数是否已被用过的数字个数。
我们先从特殊情况入手,我们假设读入为 n=12345678 K=1。
(上图为初始化状态)
其中 k对应的二进制数就是011(2)=3(10)
二进制中1的表示对应数位还没有被占用,同时距离当前数位的距离不超过K,接下来我们考虑转移。
(一) 当前行占用k二进制中的第一个1的位置
则转移为
(二) 当前行占用k二进制中的第二个1的位置
则转移为
从特殊情况中,已经看出了大致的转移方法,尝试放入k二进制中1的位置,再将整体右移处理下一位。不过在本题中还要考虑不能有前导0的情况需要进行判断,同时需要指出的是如果k的二进制中首位是1,则转移只有一种,把首位的1放入,不然之后会出现不够放的情况。
我们在考虑一种特殊情况n=3333 m=1 k=1
答案很明显是1 3333。但是如果只考虑了上述情况的转移就会出现重复的情况答案会是3
3333。接下来考虑如何避免重复。事实上,我们从高位到低位枚举的时候只枚举没有出现过的数字这样就不会重复了。
转移一:
转移二:
因为已经有过对应数位为3的转移,所以这种转移非法要忽视掉。
最后要提醒一点:dp出来的值为0 不一定是0还可能是10007的倍数需要仔细处理。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef int LL; LL dp[1005][105][130]; pair<int,int> g[1005][105][130]; LL to[1005][105][130]; const LL mod=10007; char s[1005]; LL a[1005],len,fil,m,k,tem,x,y,newx,newy; //======================================== LL dfs(LL o,LL mo,LL p){ LL &now=dp[o][mo][p]; if(~now)return now; if(o==len)return now=(mo==0); now=0; if((p>>(2*k))&1){ LL tar=dfs(o+1,(mo*10+a[o-k])%m,fil&(p<<1|(o+k+1<len))); if(tar){ to[o][mo][p]=a[o-k]; g[o][mo][p]=make_pair((mo*10+a[o-k])%m,fil&(p<<1|(o+k+1<len))); now=((now+tar+10006)%mod)+1; } }else{ bool c[10];memset(c,0,sizeof(c)); for(int i=2*k-1;i>=0;--i){ if((p>>i)&1){ if(c[a[o+k-i]])continue; c[a[o+k-i]]=1; if(o==0&&a[o+k-i]==0)continue; LL tar=dfs(o+1,(mo*10+a[o+k-i])%m,(p^(1<<i))<<1|(o+k+1<len)); if(tar){ if((to[o][mo][p]==-1)||to[o][mo][p]>a[o+k-i]){ to[o][mo][p]=a[o+k-i]; g[o][mo][p]=make_pair((mo*10+a[o+k-i])%m,(p^(1<<i))<<1|(o+k+1<len)); } now=((now+tar+10006)%mod)+1; } } } } return now; } //======================================== int main(){ LL i; while(~scanf("%s",s)){ memset(dp,-1,sizeof(dp)); memset(to,-1,sizeof(to)); len=strlen(s); for(i=0;i<len;++i)a[i]=s[i]-'0'; scanf("%d%d",&k,&m); k=min(k,len-1); if(k==0){ tem=0; for(i=0;i<len;++i)tem=(tem*10+a[i])%m; if(tem){ printf("0 -1\n"); }else{ printf("1 "); for(i=0;i<len;++i)printf("%d",a[i]); printf("\n"); } continue; } fil=(1<<(2*k+1))-1; printf("%d ",(dfs(0,0,(1<<(k+1))-1))%mod); x=0;y=(1<<(k+1))-1; if(to[0][x][y]==-1){ printf("-1\n"); continue; } for(i=0;i<len;++i){ printf("%d",to[i][x][y]); newx=g[i][x][y].first; newy=g[i][x][y].second; x=newx;y=newy; } printf("\n"); } return 0; }
相关文章推荐
- Django-blog-zinnia初体验(一)
- 设置笔记本与台式电脑网络共享
- React-Native 学习之 Flex布局
- java定时任务代理配置
- lightoj1116 - Ekka Dokka【数学】
- 组件关联映射
- hibernate链接数据库链接池c3p0配置
- DTP中写例程 取前7日和前1日
- hibernate链接数据库链接池c3p0配置
- hibernate链接数据库链接池c3p0配置
- weight decay (权值衰减)
- Linux调度器性能分析 - 1
- 消息队列选型[首选Kafka](备选:RabbitMQ/NSQ/RocketMQ/disque/Kafka)
- C++11:右值引用和转发型引用
- Linux IPv6网络编程之UDP实例
- 你需要知道的12个Git高级命令
- github+hexo+node.js搭建个人博客基本过程及遇到的问题
- github+hexo+node.js搭建个人博客基本过程及遇到的问题
- Oracle表空间和表的区别
- Android-自定义组件-最全下拉刷新分析-推荐