LOJ 2550 「JSOI2018」机器人——找规律+DP

题目:https://loj.ac/problem/2550

只会写20分的搜索……

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5;
int n,m,ans;
bool b[N][N],vis[N][N];
void dfs(int x,int y,bool fx,int lj)
{
    if(y>m)y=1; if(x>n)x=1;
    if(vis[x][y])
    {
        if(x==1&&y==1)
        {
            bool fg=0;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                    if(!vis[i][j]){fg=1;break;}
            if(!fg)ans+=lj;
        }
        return;
    }
    if(b[x][y])fx=1; if(!fx)lj++;
    vis[x][y]=1;
    dfs(x+1,y,fx,lj); dfs(x,y+1,fx,lj);
    vis[x][y]=0;
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%1d",&b[i][j]);
        ans=0;
        dfs(1,1,0,0);
        printf("%d\n",ans);
    }
    return 0;
}
View Code

观察多篇题解:

https://blog.csdn.net/qq_39972971/article/details/80441415

https://www.cnblogs.com/cjyyb/p/10422074.html

https://blog.csdn.net/scar_lyw/article/details/80411617

由结论可知合法的方案取决于左上角 d*d 怎么决策。(副对角线可以拐,所以是 d 条而不是 2*d-1 条)

枚举 d*d 里向下 i 步,向右 j=d-i 步,那么需要 i 和 n 互质、 j 和 m 互质。这样就是合法方案。考虑已知 i , j ,算贡献。

每个位置 ( x, y ) 都会在 “一轮”(d步) 之后走到 ( x+i , y+j ) 。

( 1, 1 ) 位置第一轮走到 ( i+1 , j+1 ) 。考虑 DP 这个第一轮的走法,就知道全局的走法了。

( 1, 1 ) 只能向下走或向右走。走过位置 ( x, y ) ,意味着会在之后的轮中把 ( x+k*i , y+k*j ) 都走过。

把 “走到第一个障碍为止的步数” 改成 “走到每个障碍为止的步数中的最小值” , 一个位置 ( x, y ) 的权值就是所有 ( x+k*i , y+k*j ) 的是障碍的点的 “走到该点的步数最小值” 取 min ; 

就是要 DP 一条从 ( 1, 1 ) 到 ( i+1 , j+1 ) 的只能向下或向右走的路径,该路径贡献是路径上各点权值的最小值。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int Mn(int a,int b){return a<b?a:b;}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
const int N=55,M=N*N,mod=998244353;
int upt(int x){while(x>=mod)x-=mod;while(x<0)x+=mod;return x;}

int n,m,lm,c[N][N],dp[N][N][M],ans;
bool b[N][N];
void cz(int &x,int y){x=upt(x+y);}
void solve(int x,int y)
{
  memset(dp,0,sizeof dp);
  dp[1][1][c[1][1]]=1;
  for(int i=1;i<=x+1;i++)
    for(int j=1;j<=y+1;j++)
      for(int k=0;k<=lm;k++)
    {
      int tp=dp[i][j][k]; if(!tp)continue;
      if(i<=x)cz(dp[i+1][j][Mn(k,c[i+1][j])],tp);
      if(j<=y)cz(dp[i][j+1][Mn(k,c[i][j+1])],tp);
    }
  for(int k=1;k<=lm;k++)
    ans=(ans+(ll)k*dp[x+1][y+1][k])%mod;
}
int main()
{
  int T;scanf("%d",&T);
  while(T--)
    {
      ans=0;
      scanf("%d%d",&n,&m); lm=n*m;
      for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
      scanf("%1d",&b[i][j]);
      int g=gcd(n,m);
      for(int x=1;x<g;x++)
    {
      int y=g-x;
      if(gcd(x,n)!=1||gcd(y,m)!=1)continue;
      for(int i=1;i<=x+1;i++)
        for(int j=1;j<=y+1;j++)
          {
        int d=i+j-2;
        if(b[i][j]){c[i][j]=d;continue;}
        int tx=i+x, ty=j+y; d+=g;
        if(tx>n)tx-=n; if(ty>m)ty-=m;
        while(1)
          {
            if(b[tx][ty]||(tx==i&&ty==j))
              {c[i][j]=d;break;}
            tx+=x; ty+=y; d+=g;
            if(tx>n)tx-=n;if(ty>m)ty-=m;
          }
          }
      solve(x,y);
    }
      printf("%d\n",ans);
    }
  return 0;
}

 

posted on 2019-04-25 15:03  Narh  阅读(309)  评论(0编辑  收藏  举报

导航