UVALive 4310 Minimal Multiple

题目地址: 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。


(上图为初始化状态)

其中 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;
} 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值