zoukankan      html  css  js  c++  java
  • CF 316div2 E.Pig and Palindromes

    E. Pig and Palindromes

    Peppa the Pig was walking and walked into the forest. What a strange coincidence! The forest has the shape of a rectangle, consisting of n rows and m columns. We enumerate the rows of the rectangle from top to bottom with numbers from 1 to n, and the columns — from left to right with numbers from 1 to m. Let’s denote the cell at the intersection of the r-th row and the c-th column as (r, c).

    Initially the pig stands in cell (1, 1), and in the end she wants to be in cell (n, m). Since the pig is in a hurry to get home, she can go from cell (r, c), only to either cell (r + 1, c) or (r, c + 1). She cannot leave the forest.

    The forest, where the pig is, is very unusual. Some cells of the forest similar to each other, and some look very different. Peppa enjoys taking pictures and at every step she takes a picture of the cell where she is now. The path through the forest is considered to be beautiful if photographs taken on her way, can be viewed in both forward and in reverse order, showing the same sequence of photos. More formally, the line formed by the cells in order of visiting should be a palindrome (you can read a formal definition of a palindrome in the previous problem).

    Count the number of beautiful paths from cell (1, 1) to cell (n, m). Since this number can be very large, determine the remainder after dividing it by 109 + 7.

    Input
    The first line contains two integers n, m (1 ≤ n, m ≤ 500) — the height and width of the field.

    Each of the following n lines contains m lowercase English letters identifying the types of cells of the forest. Identical cells are represented by identical letters, different cells are represented by different letters.

    Output
    Print a single integer — the number of beautiful paths modulo 109 + 7.

    Sample test(s)

    input
    3 4
    aaab
    baaa
    abba
    output
    3

    题意概述:在一个n*m的矩阵中,每个格子都有一个字母。你从(1,1)出发前往(n,m),每次仅仅能向下或向右。当到达终点时,把你经过的字母写下来。产生一个字符串。求有多少种走成回文的方案。

    每一次仅仅能向下或向右。所以考虑能够用dp做。考虑曼哈顿距离
    按距离原点和终点的曼哈顿距离同样的两个点做状态转移
    想象有两个点分别从起点和终点同一时候向中间走
    用f[p1][p2] 表示 第一个点在p1位置第二个点在p2位置时的从起点终点同一时候走过的同样字母路径的合法状态数

    f[p1][p2]=f[p1_f1][p2_f1]+f[p1_f1][p2_f2]+f[p1_f2][p2_f1]+f[p1_f2][p2_f2]

    p1_f1,p1_f2,p2_f1,p2_f2分别表示p1和p2的前驱点

    因为坐标非常大,须要用滚动数组优化。斜着循环每个点也须要一些小技巧详细看代码~

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    using namespace std;
    const int MAX=505;
    const int MOD=1e9+7;
    char s[MAX][MAX];
    int f[MAX][MAX]={0};
    int f_[MAX][MAX];
    int i,j,m,n,k,dis;
    struct point{
        int x,y;
    };
    point next_1(point a)
    {
        if (a.y==1&&a.x<n)
            a.x++;
        else
            a.y++;
        return a;
    }
    point next_2(point a)
    {
        if (a.x==n&&a.y>1)
            a.y--;
        else
            a.x--;
        return a;
    }
    point nex(point a)
    {
        a.x--;
        a.y++;
        return a;
    }
    int main()
    {
        cin>>n>>m;
        int ans=0;
        getchar();
        for (i=1;i<=n;i++)
        {
            for (j=1;j<=m;j++)
                scanf("%c",&s[i][j]);
            getchar();
        }
        point a,b,p1,p2;
        a.x=a.y=1;
        b.x=n;b.y=m;
        int max_=(m+n)/2;
        if (s[1][1]==s[n][m])
            f[1][n]=1;
        else
            f[1][n]=0;
        if (m+n<=3)
        {
            cout<<f[1][n]<<endl;
            return 0;
        }
        for (dis=2;dis<=max_;dis++)
        {
            a=next_1(a);
            b=next_2(b);
            for (i=1;i<=500;i++)
                for (j=1;j<=500;j++)
                {
                    f_[i][j]=f[i][j];
                    f[i][j]=0;
                }
            for (p1=a;p1.y<=m&&p1.x>=1;p1=nex(p1))
                for (p2=b;p2.y<=m&&p2.x>=1;p2=nex(p2))
                    if (s[p1.x][p1.y]==s[p2.x][p2.y])
                    {
                        f[p1.x][p2.x]=((f_[p1.x-1][p2.x]+f_[p1.x-1][p2.x+1])%MOD+(f_[p1.x][p2.x]+f_[p1.x][p2.x+1])%MOD)%MOD;
                        if (((p1.x==p2.x)&&(abs(p1.y-p2.y)<=1))||((p1.y==p2.y)&&(abs(p1.x-p2.x)<=1)))
                            ans=(ans+f[p1.x][p2.x])%MOD;
                    }
        }
        cout<<ans<<endl;
        return 0;
    }
    

    贴一个cf上看到的位运算的程序,相当简短

    #include <bits/stdc++.h>
    using namespace std;
    #define f(i,n) for(int i=0;i<(n);i++)
    #define fr(i,n) for(int i=n;i--;)
    char x[500][501];
    int d[2][501][501],n,m;
    main(){
        cin>>n>>m;
        f(i,n) cin>>x[i];
        f(ei,n) fr(si,n) fr(sj,m){
            auto& c=d[ei&1][si][sj]=0,ej=n+m-2-si-sj-ei;
            if(si<=ei&&sj<=ej&&x[si][sj]==x[ei][ej]&&!(c=abs(si-ei)+abs(sj-ej)<=1))
                f(i,2) f(j,2) c=(c+d[ei-!j&1][si+!i][sj+!!i])%((int)1e9+7);
        }
        cout<<d[~n&1][0][0]<<'
    ';
    }
  • 相关阅读:
    渗透测试中的文件传输通道1- cmd下下载文件
    内网渗透常用命令
    技术剖析中国菜刀原理
    win8 iis安装及网站发布
    C++与C的区别一
    C语言实现单链表,并完成链表常用API函数
    C语言实现数组及链表的快速排序
    使用C语言封装数组,动态实现增删改查
    C语言中静态断言的使用
    二分查找法C语言实现
  • 原文地址:https://www.cnblogs.com/lytwajue/p/7257021.html
Copyright © 2011-2022 走看看