2018 Multi-University Training Contest 9: B. Rikka with Seam(DP)

本文介绍了一种新颖的图像处理算法——K-缝图像缩放算法,该算法通过选择性地移除图像中的像素行来实现图像的尺寸调整,同时确保保留尽可能多的源图像信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

Rikka with Seam

Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 164    Accepted Submission(s): 50

Problem Description

Seam carving is a novel algorithm for resizing images while maintaining as much information as possible from the source image.
Now, Rikka is going to use seam carving method to deal with an n×m black and white picture. We can abstract this picture into an n×m 01 matrix A.
A K-seam of this picture is an integer sequence a which satisfies the following conditions:
1. |a|=n, ai∈[1,m].
2. |ai−ai+1|≤K, ∀i∈[1,n).
After choosing a K-seam a, Rikka reduces the size of the picture by deleting pixels (i,ai), and then she gets a matrix A′ of size n×(m−1).

Rikka finds that deleting different seams may get the same result. In the previous example, seam [1,2,3],[1,2,1],[1,2,2],[1,1,1] are equivalent.
Now Rikka wants to calculate the number of different matrixes she can get by deleting exactly one K-seam.

Input

The first line contains a single integer t(1≤t≤103), the numebr of testcases.

For each testcase, the first line contains three numbers n,m,K(2≤n,m≤2×103,1≤K≤m).

Then n lines follow, each line contains a 01 string of length m which describes one row of the matrix.

The input guarantees that there are at most 5 testcases with max(n,m)>300.

Output

For each testcase, output a single line with a single number, the answer modulo 998244353.

 

题意:

给你一个n*m大小的01矩阵,对于这个矩阵的每一行,你都必须标记其中一个位置并且将这个位置上的数字删去, 并且要求相邻两行删去的位置之间的横线距离不能超过k,这样可以得到一个n行m-1列的01矩阵,问最后能得到多少种不同的矩阵

思路:

对于每一行中任意一段连续的0或者连续的1,很显然删除其中的任意一个数字,最后的结果都是相同的,例如1100011,删除前两个1,中间三个0,或者后两个1中的其中一个,得出的结果都相同(100011、110011、110001)

可以将矩阵转化一下:对于每一行,第i个数字变成前是第几段连续的0或1,如下

这样问题就变成了,从矩阵中每一行选出一个数字,其中相邻两行选出的数字之间的横线距离不能超过k,这样可以得到一个长度为n的数字串,问可以得到多少种不同的数字串

 

因为n和m都很小(≤2000),考虑n*m的DP

  • dp[x][y]表示只考虑前x行,第x行选中了第y个数字总共能得到多少种不同的数字串
  • 很容易想到dp[x][y] = $\sum\limits_{i=y-k}^{y+k}dp[x-1][i], 然后很容易发现dp状态表示没问题,但是这个式子是错的,因为有大量重复

所以这个dp没法直接转移,怎么办?

  • 假设能求出L[x][y] = $\sum\limits_{i=p-k}^{y+k}F(i),其中p表示y所在那一段相等的数中,最左边那个数所在的位置,右边一个整体$\sum\limits_{i=p-k}^{y+k}F(i)表示上面一行从p-k 位置开始,到y+k位置结束, 以其中任意一个位置作为最后一个数字,无遗漏不重复的不同数字串个数
  • 再求出R[x][y] = $\sum\limits_{i=y-k}^{q+k}F(i),同理,其中q表示y所在那一段相等的数中,最右边那个数所在的位置

到这里可能会问:上面两个等式右边怎么求?等式左边的L[]和R[]求出来的又有什么用?

  • 想求出等式右边的部分$\sum\limits_{i=p-k}^{y+k}F(i), 就必须用到上一行左边的部分L[x][y]和R[x][y]
  • 记录q[x][y]表示x行第y段相等数字中最右边那个数所在位置的L[]值
  • 稍加计算得出:L[x][y]=$\sum\limits_{i=p-k}^{y+k}F(i)=L[x-1][y+k]+R[x-1][p-k]+\sum q[x-1][z],其中y+k ≥ z ≥ p-k
  • 同理R[x][y]=$\sum\limits_{i=y-k}^{q+k}F(i)=R[x-1][y-k]+L[x-1][q+k]+\sum q[x-1][z]
  • q[]可以用前缀和O(1)计算,搞定

 

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<string>
#include<math.h>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
#define LL long long
#define mod 998244353
char str[2005][2005];
int sum[2005][2005], L[2005][2005], R[2005][2005], bel[2005][2005];
int main(void)
{
	int T, n, m, i, j, dis, l, r, cnt, ans;
	scanf("%d", &T);
	while(T--)
	{
		scanf("%d%d%d", &n, &m, &dis);
		for(i=1;i<=n;i++)
			scanf("%s", str[i]+1);
		cnt = 0;
		for(i=1;i<=m;i++)
		{
			L[1][i] = R[1][i] = 1;
			if(str[1][i]!=str[1][i-1] || i==1)
				cnt++;
			bel[1][i] = cnt;
			sum[1][cnt] = cnt;
		}
		ans = sum[1][cnt];
		for(i=2;i<=n;i++)
		{
			cnt = 0;
			for(j=1;j<=m;j++)
			{
				if(str[i][j]!=str[i][j-1] || j==1)
					l = max(j-dis, 1), cnt++;
				bel[i][j] = cnt;
				r = min(j+dis, m);
				L[i][j] = (L[i-1][r]+R[i-1][l])%mod;
				L[i][j] = (L[i][j]+sum[i-1][bel[i-1][r]-1]-sum[i-1][bel[i-1][l]])%mod;
				if(L[i][j]<0)
					L[i][j] = (L[i][j]+mod)%mod;
				sum[i][cnt] = (sum[i][cnt-1]+L[i][j])%mod;
			}
			ans = sum[i][cnt];
			for(j=m;j>=1;j--)
			{
				if(str[i][j]!=str[i][j+1] || j==m)
					r = min(j+dis, r);
				l = max(j-dis, 1);
				R[i][j] = (L[i-1][r]+R[i-1][l])%mod;
				R[i][j] = (R[i][j]+sum[i-1][bel[i-1][r]-1]-sum[i-1][bel[i-1][l]])%mod;
				if(R[i][j]<0)
					R[i][j] = (R[i][j]+mod)%mod;
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}
/*
1
3 5 1
11111
11111
11111
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值