码迷,mamicode.com
首页 > 其他好文 > 详细

51nod 1683 最短路

时间:2020-07-05 18:58:00      阅读:56      评论:0      收藏:0      [点我收藏+]

标签:sdn   特殊   names   details   getc   turn   代码   nod   https   

题意

给定一个未知的\(0/1\)矩阵,对每个\(i\)\((1,1)\sim(n,m)\)最短路为\(i\)的概率,在矩阵中不能向左走,路径长度为路径上权值为\(1\)的格子个数。

\(n\leq6,m\leq100。\)

思路

参考了这篇博客的思路【51nod1683】最短路


概率乘了\(2^{n\times m}\)之后其实就是方案数,所以问题转化为了求满足题目条件的方案数

发现\(n\)很小,最大只有\(6\),考虑状压,但是不能直接维护当前格子的最短路,因为在多条并列最短路时会重复计数

考虑现在的\(0/1\)矩阵的特殊性:因为不能向左走,所以对于同一列中相邻两个格子之间的最短路最多相差\(1\)。因此考虑维护一整列最短路的差分数组。

\(zt\)为一个三进制状态,表示该行从第二行开始,每个格子与上面的格子的差

\(f[i][j][zt]\)表示第\(i\)列,第一行的最短路为\(j\),第\(2\)行~第\(n\)行的最短路的三进制为\(zt\)的方案数

转移时需要枚举下一列的\(0/1\)状态,线性更新一遍状态就可以了

时间复杂度为\(O(nm2^n3^{n-1})\)

代码

/*
Author:loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;

const int A = 111;
const int B = 1e6 + 11;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar();
	int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == ‘-‘) f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int zt[7], d[7], r[7], w[7], qwq, ans[A];
int n, m, mod, f[A][A][A << 3], g[A << 3][1 << 6][2];

signed main() {
	n = read(), m = read(), mod = read();
	qwq = (1 << n) - 1, zt[0] = 1;
	for (int i = 1; i <= n; i++) zt[i] = zt[i - 1] * 3;
	for (int s1 = 0; s1 <= zt[n - 1] - 1; s1++) {
		d[1] = 0;
		for (int i = 1; i <= n - 1; i++)
			d[i + 1] = d[i] + (s1 / zt[i - 1] % 3 - 1);
		for (int s2 = 0; s2 <= qwq; s2++) {
			for (int i = 1; i <= n; i++)
				w[i] = ((s2 & (1 << i - 1)) > 0), r[i] = d[i] + w[i];
			for (int i = 2; i <= n; i++) r[i] = min(r[i], r[i - 1] + w[i]);
			for (int i = n - 1; i >= 1; i--) r[i] = min(r[i], r[i + 1] + w[i]);
			g[s1][s2][0] = r[1];
			for (int i = 2; i <= n; i++)
				g[s1][s2][1] += (r[i] - r[i - 1] + 1) * zt[i - 2];
		}
	}
	memset(d, 0, sizeof(d));
	for (int s = 0; s <= qwq; s++) {
		for (int i = 1; i <= n; i++) d[i] = d[i - 1] + ((s & (1 << i - 1)) > 0);
		int t = 0;
		for (int i = 2; i <= n; i++) t += (d[i] - d[i - 1] + 1) * zt[i - 2];
		f[1][d[1]][t]++;
	}
//	for (int i = 1; i <= m; i++) cout << d[i] << " ";
//	puts("");
//	for (int i = 1; i <= m; i++) cout << w[i] << " ";
//	puts("");
//	for (int i = 1; i <= m; i++) cout << r[i] << " ";
//	puts("");
	for (int i = 1; i <= m - 1; i++)
		for (int j = 0; j <= i; j++)
			for (int s = 0; s <= zt[n - 1] - 1; s++) {
				if (!f[i][j][s]) continue;
				for (int x = 0; x <= (1 << n) - 1; x++) 
					(f[i + 1][g[s][x][0] + j][g[s][x][1]] += f[i][j][s]) %= mod;
			}
//	for (int i = 1; i <= m; i++) {
//		for (int j = 1; j <= m; j++) {
//			for (int s = 0; s <= qwq; s++) {
//				cout << f[i][j][s] << " ";
//			}
//			puts("");
//		}
//		puts("");
//	}
	for (int j = 0; j <= m; j++)
		for (int s = 0; s <= zt[n - 1] - 1; s++) {
			if (!f[m][j][s]) continue;
			d[1] = j;
			for (int i = 1; i <= n - 1; i++)
				d[i + 1] = d[i] + (s / zt[i - 1] % 3 - 1);
			if (d[n] < 0) continue;
			(ans[d[n]] += f[m][j][s]) %= mod;
		}
	for (int i = 0; i <= n + m - 1; i++) cout << ans[i] << ‘\n‘;
	return 0;
}
/*
2 2 100000
*/

51nod 1683 最短路

标签:sdn   特殊   names   details   getc   turn   代码   nod   https   

原文地址:https://www.cnblogs.com/loceaner/p/13247062.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!