Codeforces Round #583 D Treasure Island 双hash dp

Treasure Island

题意:在一个 n * m 的矩阵中, ' . ' 代表可以通行, ' # ' 代表障碍,每次只能向右或者向下走, 让你选择最少的点将它们变成障碍, 使不存在(1, 1)到  (n,m)的通路, (1,1)和(n,m)不能是障碍;

题解:由“显然定理”可知, 答案只有三种情况(0、1、2), 最多我只需要将(n-1,m),(n,m-1) 变成障碍,那么就不存在(1, 1)到  (n,m)的通路。

  • 答案为 0 , 那么说明不存在(1, 1)到  (n,m)的通路, 很多方法可以判断。
  • 现在只需要判断出答案为 1 或者答案为 2 的情况。 答案为 1 ,显然,从(1, 1)到  (n,m)的所有通路,都经过 某一个点。
  • 现在问题转化为了求经过任意一点的(1, 1)到  (n,m)通路的个数。
  • dp[i][j] 表示从(1,1)到(i,j)的方案数,d[i][j] 表示从(n,m)到(i,j)的方案数, 任意一点的(1, 1)到  (n,m)通路的个数 = dp[i][j] * d[i][j]; if dp[i][j] * d[i][j] = dp[0][0] * d[0][0], 答案为 1;
  • 由于 矩阵比较大, 所以 dp 肯定会爆 ll , 需要hash一下
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+10;
int n, m;
int mod[2] = {100000007, 1000000007};
char *s[N];
int *g[N];
ll *d[2][N], *dp[2][N];

void init(){
	for(int i = 0; i <= n+1; i++)
		for(int j = 0; j <= m+1; j++)
			for(int k = 0; k < 2; k++)
				d[k][i][j] = dp[k][i][j] = 0;
}
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for(int i = 0; i <= n+1; i++){
		s[i] = new char[m+10];
		for(int j = 0; j < 2; j++){
			d[j][i] = new long long [m+10];
			dp[j][i] = new long long [m+10];
		}
	}
	init();
	for(int i = 1; i <= n; i++){
		cin >> s[i]+1;
	}
	d[0][n][m+1] = 1;
	d[1][n][m+1] = 1;
	dp[0][0][1] = 1;
	dp[1][0][1] = 1;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			if(s[i][j] == '#') continue;
			dp[0][i][j] = (dp[0][i-1][j] + dp[0][i][j-1]) % mod[0];
			dp[1][i][j] = (dp[1][i-1][j] + dp[1][i][j-1]) % mod[1];
		}
	}
	for(int i = n; i >= 1; i--){
		for(int j = m; j >= 1; j--){
			if(s[i][j] == '#') continue;
			d[0][i][j] = (d[0][i+1][j] + d[0][i][j+1]) % mod[0];
			d[1][i][j] = (d[1][i+1][j] + d[1][i][j+1]) % mod[1];
		}
	}
	if(dp[0][n][m] == 0 && dp[1][n][m] == 0){
		cout << "0\n";
		return 0;
	}
	ll tmp1 = dp[0][1][1] * d[0][1][1] % mod[0];
	ll tmp2 = dp[1][1][1] * d[1][1][1] % mod[1];
	bool flag = 0;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			if(i == 1 && j == 1) continue;
			if(i == n && j == m) continue;
			if(dp[0][i][j]*d[0][i][j]%mod[0]==tmp1 && dp[1][i][j]*d[1][i][j]%mod[1]==tmp2 ){
				flag = 1;
				break;
			}
		}
		if(flag) break;
	}
	if(flag)
		cout << "1\n";
	else cout << "2\n";
	return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值