Rikka with Seam (DP+前缀和优化)

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6416]


题目描述:

给定一个 NM N ∗ M 的01矩阵

K-seam是满足以下条件的整数序列a:

  • |a|=n,ai[1,m] | a | = n , a i ∈ [ 1 , m ]
  • |aiai+1|K,i[1,n) | a i − a i + 1 | ≤ K , i ∈ [ 1 , n )

对于第 i i 行,删除第 ai a i 个数,会得到一个 N(M1) N ∗ ( M − 1 ) 的矩阵

不同的整数序列可能会得到相同的矩阵

问:可以得到多少种不同的矩阵


题解:

如果无视different的条件
定义状态: dp[i][j] d p [ i ] [ j ] ——删除第 i i 行第 j j 个数可以得到的矩阵总数
可推得转移方程如下:

dp[i][j]=x=jkj+kdp[i1][x] d p [ i ] [ j ] = ∑ x = j − k j + k d p [ i − 1 ] [ x ]

在此基础上,加上different的条件
定义状态:

  • tot[i][j] t o t [ i ] [ j ] ——删除第 i i 行第 j j 个数可以得到的不同的矩阵总数
  • same[i][j] s a m e [ i ] [ j ] ——删除第 i i 行第 j j 个数和删除第 i i 行第 j1 j − 1 个数后得到的相同方案数

那么, tot[i][j] t o t [ i ] [ j ] 的状态转移方程为:

tot[i][j]=x=jkj+ktot[i1][x]x=jk+1j+ksame[i1][x] t o t [ i ] [ j ] = ∑ x = j − k j + k t o t [ i − 1 ] [ x ] − ∑ x = j − k + 1 j + k s a m e [ i − 1 ] [ x ]

如果 Matrix[i][j]Matrix[i][j1] M a t r i x [ i ] [ j ] ≠ M a t r i x [ i ] [ j − 1 ] 那么, same[i][j]=0 s a m e [ i ] [ j ] = 0
否则,对于第 j j 个和第 j1 j − 1 个的转移区间分别为 [jk,j+k] [ j − k , j + k ] [j1k,j1+k] [ j − 1 − k , j − 1 + k ] ,那么它们的交叉区间为 [jk,j1+k] [ j − k , j − 1 + k ] ,即:从该区间内转移得到的方案数会重复计算一次。得到 same[i][j] s a m e [ i ] [ j ] 的转移方程为:
same[i][j]=x=jkj+k1tot[i1][x]x=jk+1j+k1same[i1][x] s a m e [ i ] [ j ] = ∑ x = j − k j + k − 1 t o t [ i − 1 ] [ x ] − ∑ x = j − k + 1 j + k − 1 s a m e [ i − 1 ] [ x ]

时间复杂度为 O(nm2) O ( n ∗ m 2 ) ,对于 做前缀和优化,时间复杂度降为 O(nm) O ( n ∗ m )

最后,对于第n行计算总的不同的方案数,即 ans=totsame a n s = ∑ t o t − ∑ s a m e


AC代码:

#include
using namespace std;
#define bll long long
const int mod = 998244353;
const int maxn = 2020;
int T,n,m,k;
bll tot[maxn][maxn],same[maxn][maxn];
bll pre_tot[maxn],pre_same[maxn];
char a[maxn][maxn];

int main()
{
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d %d %d",&n,&m,&k);
        for (int i=1;i<=n;i++) scanf("%s",a[i]+1);

        for (int j=1;j<=m;j++)
        {
            tot[1][j] = 1;
            if (j > 1 && a[1][j] == a[1][j-1]) same[1][j] = 1;
            else
                same[1][j] = 0;
        }

        for (int i=2;i<=n;i++)
        {
            pre_tot[0] = pre_same[0] = 0;
            for (int j=1;j<=m;j++)
            {
                pre_tot[j] = (pre_tot[j-1]+tot[i-1][j]) % mod;
                pre_same[j] = (pre_same[j-1]+same[i-1][j]) % mod;
            }

            int L,R;
            for (int j=1;j<=m;j++)
            {
                L = max(1,j-k),R = min(m,j+k);
                tot[i][j] = ((pre_tot[R]-pre_tot[L-1]+ mod)%mod - (pre_same[R]-pre_same[L]+mod)%mod+mod) % mod;

                if (j == 1 || a[i][j] != a[i][j-1])
                {
                    same[i][j] = 0;
                    continue;
                }

                R = min(m,j+k-1);
                same[i][j] = ((pre_tot[R]-pre_tot[L-1]+mod)%mod - (pre_same[R]-pre_same[L]+mod)%mod + mod) % mod;
            }
        }

        long long ans = tot[n][1];
        for (int j = 2; j <= m; j++)
            ans = (ans + tot[n][j] - same[n][j] + mod) % mod;
        printf("%lld\n",ans);
    }
    return 0;
}

你可能感兴趣的:(动态规划)