loj2550/洛谷P4558/bzoj5318 「JSOI2018」机器人 性质分析+DP

机器人行走问题的DP决策分析
博客围绕机器人行走问题展开分析。指出同一对角线行为决策须一致,行走有循环性,存在长度为d(d=gcd(n,m))的循环节。要到达每行每列需满足gcd(dx,n)=1和gcd(dy,m)=1。通过枚举dx确定dy,再确定循环内决策,最后进行DP决策,分析了复杂度为O(n5)。

题目分析

首先,同一根对角线上的行为决策必须一样(要么都往右要么都往下)。
灵魂画手litble
行走是循环的,走到最右就变成最左,走到最左就变成最右了,所以“一根对角线”的意义也是循环的,比如说下图每一种颜色的点都属于同一根对角线。
灵魂画手litble
假设有一根对角线,对于它上面的每一个点(x,y)(x,y)(x,y)x+y=kx+y=kx+y=k。当(x,y)(x,y)(x,y)出了边界后,为了使它拥有合法的意义,需要(x+=nx+=nx+=nx−=nx-=nx=ny+=my+=my+=my−=my-=my=m),不难发现,最后所有满足x+y=k+an+bmx+y=k+an+bmx+y=k+an+bma,ba,ba,b为整数)的点都在这根对角线上。根据裴蜀定理,设d=gcd(n,m)d=gcd(n,m)d=gcd(n,m),所有满足x+y=k+tdx+y=k+tdx+y=k+tdddd为整数)的点都在这根对角线上。因此,一共有ddd条不同的对角线。

而机器人每走一步,都一定会到达一根新的对角线,所以机器人的行走决策,存在长度为ddd的循环节。

假设对于每个ddd循环节,选择“向下走”走了dxdxdx步,“向右走”走了dydydy步。显然要gcd(dx,n)=1gcd(dx,n)=1gcd(dx,n)=1,才能到达每一行。要gcd(dy,m)=1gcd(dy,m)=1gcd(dy,m)=1,才能到达每一列。至于充分性……呃,意会(捂脸)……

枚举dxdxdxdydydy也已知了,接下来的任务就只有确定循环内的决策了。

假设钦定机器人是在第PPP步决策撞到障碍的,假设我钦定了前PPP步决策是向下走xxx步,向右走yyy步,那么它撞到的障碍物的坐标应该被写成(1+x+k×dx,1+y+k×dy)(1+x+k \times dx,1+y+k \times dy)(1+x+k×dx,1+y+k×dy)的形式,且kkk要尽可能小。于是,设(1+x,1+y)(1+x,1+y)(1+x,1+y)点的“权值”为x+y+k×dx+y+k \times dx+y+k×d,即钦定在这步决策导致撞障碍物情况下,最早撞障碍物的时间。

接下来开始DP决策,设f(i,j,k)f(i,j,k)f(i,j,k)表示从(1,1)(1,1)(1,1)走到(i,j)(i,j)(i,j)这个点(即钦定走了i−1i-1i1步向下,j−1j-1j1步向右),走到的最小权值为kkk(即已经钦定好了的决策导致最早的撞墙时间)的方案数。DP转移就是选择向下还是向右,最后将所有的k×f(dx+1,dy+1,k)k \times f(dx+1,dy+1,k)k×f(dx+1,dy+1,k)加入答案中。

复杂度分析:k≤n∗mk \leq n*mknm,DP复杂度O(n4)O(n^4)O(n4),枚举dxdxdx复杂度O(n)O(n)O(n),计算权值复杂度O(n4)O(n^4)O(n4),总复杂度O(n5)O(n^5)O(n5)

代码

#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int mod=998244353;
int T,n,m,d,ans,f[52][52][2502],val[52][52];
char mp[52][52];

int qm(int x) {return x>=mod?x-mod:x;}
int gcd(int x,int y) {return y?gcd(y,x%y):x;}
int DP(int dx,int dy) {
	for(RI i=1;i<=dx+1;++i)
		for(RI j=1;j<=dy+1;++j)
			for(RI k=1;k<=n*m;++k) f[i][j][k]=0;
	f[1][1][val[1][1]]=1;
	for(RI i=1;i<=dx+1;++i)
		for(RI j=1;j<=dy+1;++j) 
			for(RI k=1;k<=n*m;++k) {
				if(!f[i][j][k]) continue;
				if(i<=dx) f[i+1][j][min(k,val[i+1][j])]=
					qm(f[i+1][j][min(k,val[i+1][j])]+f[i][j][k]);
				if(j<=dy) f[i][j+1][min(k,val[i][j+1])]=
					qm(f[i][j+1][min(k,val[i][j+1])]+f[i][j][k]);
			}
	int re=0;
	for(RI i=1;i<=n*m;++i) re=qm(re+1LL*i*f[dx+1][dy+1][i]%mod);
	return re;
}

int main()
{
	scanf("%d",&T);
	while(T--) {
		scanf("%d%d",&n,&m),d=gcd(n,m),ans=0;
		for(RI i=1;i<=n;++i) scanf("%s",mp[i]+1);
		for(RI dx=1;dx<=d;++dx) {
			if(gcd(dx,n)!=1||gcd(d-dx,m)!=1) continue;
			for(RI x=1;x<=dx+1;++x)
				for(RI y=1;y<=d-dx+1;++y) {
					int tx=x,ty=y,nowd=x-1+y-1;val[x][y]=n*m;
					do {
						if(mp[tx][ty]=='1') {val[x][y]=nowd;break;}
						tx+=dx,ty+=d-dx,nowd+=d;
						if(tx>n) tx-=n; if(ty>m) ty-=m;
					}while(tx!=x||ty!=y);
				}
			ans=qm(ans+DP(dx,d-dx));
		}
		printf("%d\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值