BZOJ1814 Ural 1519 Formula 1

插头dp的练手题么。虽然之前还没写过哈密顿回路的题。

 

学习了一下广义括号法。发现状态数比之前记连通性的方法要少。仔细想想发现,两个连通的插头的顺序有关。不能存在1212这种东西。所以连通性的方法记了很多废状态。那把连通性方法改进一下会不会变得更快呢?可以研究一下。我觉得应该是等效了吧。

 

今天的debug时间明显缩短了。虽然还是debug了老半天。然后怎么代码还是那么长。想起我每次都觉得把东西挤到一起看起来很不雅观,于是去写一堆找转移的函数。晕。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long dint;
#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif
#define _l (long long int)

const int maxn = 15;
const int maxst = 50009;

#define mbit(_x_,_y_) ((_x_)<<((_y_)<<1))
#define gbit(_x_,_y_) (((_x_)>>((_y_)<<1))&3)
#define cbit(_x_,_y_) ((_x_)&(~(3<<((_y_)<<1))))

int n, m, tots, slst[maxst];
bool mp[maxn][maxn];
dint f[2][maxst];

void dfsState(int l, int c, int z) {
	if (l == m + 1) {
		if (!c)
			slst[tots ++] = z;
	}
	else {
		dfsState(l + 1, c, z);
		dfsState(l + 1, c + 1, z | mbit(1, l));
		if (c)
			dfsState(l + 1, c - 1, z | mbit(2, l));
	}
}
void preState() {
	tots = 0;
	dfsState(0, 0, 0);
	sort(slst, slst + tots);
}
inline int fState(int s) {
	register int ret(lower_bound(slst, slst + tots, s) - slst);
	if (slst[ret] != s)
		puts("naive");
	return ret;
}

#define inc(_a_,_b_) (_a_+=_b_)
inline int brv(int x) {
	if (!x)
		return 0;
	else
		return (x == 1) ? 1 : -1;
}

bool isEnd(int s, int y) {
	for (int i = 0; i < y; ++ i)
		if (gbit(s, i))
			return 0;
	for (int i = y + 2; i <= m; ++ i)
		if (gbit(s, i))
			return 0;
	return 1;
}
inline int mergePlug(int s, int y) {
	int p, c;
	if (gbit(s, y) != gbit(s, y + 1)) {
		s = cbit(s, y);
		s = cbit(s, y + 1);
		return fState(s);
	}
	else if (gbit(s, y) == 1) {
		for (c = 1, p = y + 1; c; ++ p, c += brv(gbit(s, p)));
		s = cbit(s, y);
		s = cbit(s, y + 1);
		s = cbit(s, p);
		return fState(s | mbit(1, p));
	}
	else {
		for (c = -1, p = y; c; -- p, c += brv(gbit(s, p)));
		s = cbit(s, y);
		s = cbit(s, y + 1);
		s = cbit(s, p);
		return fState(s | mbit(2, p));
	}
}
inline int extPlugRight(int s, int y) {
	s |= mbit(gbit(s, y), y + 1);
	s = cbit(s, y);
	return fState(s);
}
inline int extPlugDown(int s, int y) {
	s |= mbit(gbit(s, y + 1), y);
	s = cbit(s, y + 1);
	return fState(s);
}
inline int forkPlug(int s, int y) {
	s |= mbit(1, y);
	s |= mbit(2, y + 1);
	return fState(s);
}
inline int nextRow(int s) {
	int r(0);
	for (int i = 0; i < m; ++ i)
		r |= mbit(gbit(s, i), i + 1);
	return fState(r);
}

dint dp() {
	int cur(0), prv(1);
	dint ans;
	memset(f, 0, sizeof(f));
	f[cur][0] = 1;
	for (int x = 0; x < n; ++ x) {
		for (int y = 0; y < m; ++ y) {
			if (mp[x][y])
				ans = 0;
			swap(cur, prv);
			memset(f[cur], 0, sizeof(f[cur]));
			for (int i = 0; i < tots; ++ i)
				if (f[prv][i]) {
					int s(slst[i]);;
					if (mp[x][y]) {
						if (gbit(s, y) && gbit(s, y + 1)) {
							if (gbit(s, y) == 1 && gbit(s, y + 1) == 2) {
								if (isEnd(s, y))
									inc(ans, f[prv][i]);
							}
							else
								inc(f[cur][mergePlug(s, y)], f[prv][i]);
						}
						else if (gbit(s, y)) {
							inc(f[cur][i], f[prv][i]);
							inc(f[cur][extPlugRight(s, y)], f[prv][i]);
						}
						else if (gbit(s, y + 1)) {
							inc(f[cur][i], f[prv][i]);
							inc(f[cur][extPlugDown(s, y)], f[prv][i]);
						}
						else
							inc(f[cur][forkPlug(s, y)], f[prv][i]);
					}
					else {
						if (!gbit(s, y) && !gbit(s, y + 1))
							inc(f[cur][i], f[prv][i]);
					}
				}
		}
		swap(cur, prv);
		memset(f[cur], 0, sizeof(f[cur]));
		for (int i = 0; i < tots; ++ i)
			if (f[prv][i] && !gbit(slst[i], m))
				inc(f[cur][nextRow(slst[i])], f[prv][i]);
	}
	return ans;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &m);
	for (int i = 0; i < n; ++ i) {
		char g[maxn];
		scanf("%s", g);
		for (int j = 0; j < m; ++ j)
			mp[i][j] = (g[j] == '.');
	}
	preState();
	printf(lld "\n", dp());
}

BZOJ2704 旅游

居然网上都没有找到程序来和我拍。好吧其实它是一道权限题而且过的人也不多。

 

其实就是裸的插头DP。本来想学一下广义括号的然后被状态数吓到了于是只好还是写最小表示。连通性。

 

然后我发现最小表示连通性很容易写挂。而在findstate的时候检查一下是个不错的办法。至少这道题靠这个就可以直接debug出来问题了。

 

比上次写插头要好许多了。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long dint;
#define _l (long long int)
#define mbit(_x_,_y_) ((_l _x_)<<((_y_)<<2))
#define gbit(_x_,_y_) (((_x_)>>((_y_)<<2))&0xf)

const int maxn = 13;
const int maxst = 570009;

dint slst[maxst];
int n, m, v[maxn][maxn], tots, f[2][maxst], cnt[17];
bool av[2][maxst];

void dfsState(int l, int tot, dint z) {
	if (l == m) {
		for (int i = 1; i <= tot; ++ i)
			if (cnt[i] != 2)
				return;
		slst[tots ++] = z;
	}
	else {
		for (int i = 0; i <= tot; ++ i)
			if (!i || cnt[i] == 1) {
				++ cnt[i];
				dfsState(l + 1, tot, z | mbit(i, l));
				-- cnt[i];
			}
		++ cnt[tot + 1];
		dfsState(l + 1, tot + 1, z | mbit(tot + 1, l));
		-- cnt[tot + 1];
		if (!cnt[15] && l < m - 1) {
			++ cnt[15];
			dfsState(l + 1, tot, z | mbit(15, l));
			-- cnt[15];
		}

	}
}

void preState() {
	tots = 0;
	memset(cnt, 0, sizeof(cnt));
	dfsState(0, 0, 0);
	sort(slst, slst + tots);
}

inline int fState(dint x) {
	return lower_bound(slst, slst + tots, x) - slst;
	//if (slst[ret] != x)
		//puts("naive");
	//return ret;
}

#define fUpMax(_sa_,_sv_) { \
	register int _a_(_sa_), _v_(_sv_); \
	if (!av[cur][_a_]) { \
		av[cur][_a_] = 1; \
		f[cur][_a_] = _v_; \
	} \
	else if (_v_ > f[cur][_a_]) \
		f[cur][_a_] = _v_; \
}
#define upMax(_a_,_b_) { \
	if (_a_ < _b_) \
		_a_ = _b_; \
}

void unzipState(dint z, int* a) {
	for (int i = 0; i < m; ++ i)
		a[i] = gbit(z, i);
}

dint zipState(int* a) {
	dint s(0);
	for (int i = 0; i < m; ++ i)
		s |= mbit(a[i], i);
	return s;
}

bool isFinalPlug(int *a, int y) {
	for (int i = 0; i < y - 1; ++ i)
		if (a[i])
			return 0;
	for (int i = y + 1; i <m; ++ i)
		if (a[i])
			return 0;
	return 1;
}

int mergePlug(int* a, int y) {
	static int b[maxn], c[17];
	memcpy(b, a, sizeof(b));
	if (a[y - 1] == 15) {
		b[y - 1] = b[y];
		b[y] = 0;
	}
	else {
		int f(a[y - 1]), t(a[y]);
		for (int i = 0; i < m; ++ i)
			if (b[i] == f)
				b[i] = t;
		b[y - 1] = b[y] = 0;
		memset(c, 0, sizeof(c));
		f = 0;
		for (int i = 0; i < m; ++ i)
			if (b[i]) {
				if (c[b[i]])
					b[i] = c[b[i]];
				else
					b[i] = c[b[i]] = ++ f;
			}
	}
	return fState(zipState(b));
}

int extendPlugRight(int* a, int y) {
	static int b[maxn];
	memcpy(b, a, sizeof(b));
	if (a[y - 1] == 15) {
		int t(1);
		for (int i = 0; i < y - 1; ++ i)
			if (a[i] < 15 && a[i] + 1 > t)
				t = a[i] + 1;
		for (int i = y + 1; i < m; ++ i)
			if (a[i] < 15 && a[i] >= t)
				++ b[i];
		b[y - 1] = b[y] = t;
	}
	else {
		b[y] = b[y - 1];
		b[y - 1] = 0;
	}
	return fState(zipState(b));
}

int newPlug(int* a, int y) {
	static int b[maxn];
	memcpy(b, a, sizeof(b));
	b[y] = 15;
	return fState(zipState(b));
}

int dp() {
	int prv(0), cur(1), so(fState(15)), ans(0);
	memset(av, 0, sizeof(av));
	av[cur][so] = 1;
	f[cur][so] = v[0][0];
	for (int x = 0; x < n; ++ x)
		for (int y = !x ? 1 : 0; y < m; ++ y) {
			swap(prv, cur);
			memset(av[cur], 0, sizeof(av[cur]));
			for (int i = 0; i < tots; ++ i)
				if (av[prv][i]) {
					dint s(slst[i]);
					static int a[maxn];
					unzipState(s, a);
					if (v[x][y]) {
						if (y && a[y - 1] && a[y]) {
							if (a[y - 1] == a[y]) {
								if (isFinalPlug(a, y))
									upMax(ans, f[prv][i] + v[x][y]);
							}
							else {
								fUpMax(mergePlug(a, y), f[prv][i] + v[x][y]);
							}
						}
						if (y && a[y - 1] && !a[y])
							fUpMax(extendPlugRight(a, y), f[prv][i] + v[x][y]);
						if (a[y] && (!y || a[y - 1] != 15)) 
							fUpMax(i, f[prv][i] + v[x][y]);
						if (!a[y] && (!y || a[y - 1] != 15) && y < m - 1)
							fUpMax(newPlug(a, y), f[prv][i] + v[x][y]);
					}
					if (!a[y] && (!y || a[y - 1] != 15))
						fUpMax(i, f[prv][i]);
				}
		}
	return ans;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	bool sw(0);
	scanf("%d%d", &n, &m);
	if (n < m)
		sw = 1;
	for (int i = 0; i < n; ++ i)
		for (int j = 0; j < m; ++ j)
			if (sw)
				scanf("%d", v[j] + i);
			else
				scanf("%d", v[i] + j);
	if (sw)
		swap(n, m);
	preState();
	printf("%d\n", dp());
}

BZOJ1187 [HNOI2007]神奇游乐园

我的第二道插头DP。差不多纠结了一天。

 

这道题网上的题解都讲的好简略啊。最后还得自己yy。没有用括号序列法让我觉得自己比较厉害。

 

考虑轮廓线上的m个格子,有3种情况:没有插头,有且只能往一边走,有且要往两边走。对于第二种,要用最多3个不同的值来记录连通性。还要有两个不同的值来表示第一种情况和第三种情况。我比较懒,所以直接用一个八进制数去表示,然后每次二分找编号。

 

考虑转移,分一堆情况进行讨论。默认从左上朝右下走。

 

首先是要走当前格子的情况。

 

如果左边和上面都有插头,那么看它们是否连通。如果不连通则把它们连通,如果已经连通,看是否还有其它插头。没有就更新答案,否则忽略。

 

如果只有左边有插头,再分类。如果它是第二种,那么这个格子可以新建一个第三种插头,或者把左边格子的插头接过来。如果它是第三种那么它必需把左边格子接过来。

 

如果只有上面有插头,那么必需把上面的插头接上来。

 

如果左边和上面都没有插头,那么可以新建一个第三种插头。

 

然后如果不走这个格子的话,要求上面没有插头,且左边不是第三种插头。

 

按mhy的话说,插头dp就是写起来很麻烦。的确。不过写出来也还是挺高兴的。

 

然后代码丑到不能看了。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 109;
const int maxm = 9;
const int maxst = 50009;
const int inf = 0x3f3f3f3f;

#define pow8(_x_) (1<<(_x_)<<(_x_)<<(_x_))
#define mbit(_x_,_y_) ((_x_)<<(_y_)<<(_y_)<<(_y_))
#define gbit(_x_,_y_) (((_x_)>>(_y_)>>(_y_)>>(_y_))&7)

int f[2][maxst], n, m, a[maxn][maxm], ans;
int slst[maxst], tots;

inline void upMax(int& a, int b) {
	if (a < b)
		a = b;
}

void dfsState(int cur, int tot, int val, int e) {
	if (cur == m) {
		static int cnt[maxm];
		memset(cnt, 0, sizeof(cnt));
		for (int i = 0; i < m; ++ i)
			++ cnt[gbit(val, i)];
		bool lg(1);
		for (int i = 1; i <= tot && lg; ++ i)
			if (cnt[i] != 0 && cnt[i] != 2)
				lg = 0;
		if (lg)
			slst[tots ++] = val;
	}
	else {
		for (int i = 0; i <= tot; ++ i)
			dfsState(cur + 1, tot, val | mbit(i, cur), e);
		dfsState(cur + 1, tot + 1, val | mbit(tot + 1, cur), e);
		if (e)
			dfsState(cur + 1, tot, val | mbit(7, cur), 0);
	}
}
void preState() {
	tots = 0;
	dfsState(0, 0, 0, 1);
	sort(slst, slst + tots);
}

int fState(int s) {
	int ret(lower_bound(slst, slst + tots, s) - slst);
	if (slst[ret] != s)
		puts("naive");
	return ret;
}

int joinState(int s, int p) {
	static int z[maxm], y[maxn], c;
	for (int i = 0; i < m; ++ i)
		z[i] = gbit(s, i);
	if (z[p - 1] == 7) {
		z[p - 1] = z[p];
		z[p] = 0;
	}
	else {
		int f(z[p]), t(z[p - 1]);
		for (int i = 0; i < m; ++ i)
			if (z[i] == f)
				z[i] = t;
		z[p - 1] = 0;
		z[p] = 0;
		memset(y, 0, sizeof(y));
		c = 0;
		for (int i = 0; i < m; ++ i)
			if (z[i] && z[i] < 7) {
				if (y[z[i]]) {
					z[i] = y[z[i]];
				}
				else {
					y[z[i]] = ++ c;
					z[i] = y[z[i]];
				}
			}
	}
	int f(0);
	for (int i = 0; i < m; ++ i)
		f |= mbit(z[i], i);
	return fState(f);
}

int expState(int s, int p) {
	static int z[maxm];
	for (int i = 0; i < m; ++ i)
		z[i] = gbit(s, i);
	int f(1);
	for (int i = 0; i < p - 1; ++ i)
		if (z[i] < 7 && z[i] + 1 > f)
			f = z[i] + 1;
	for (int i = 0; i < m; ++ i)
		if (z[i] < 7 && z[i] >= f)
			++ z[i];
	z[p - 1] = f;
	z[p] = f;
	f = 0;
	for (int i = 0; i < m; ++ i)
		f |= mbit(z[i], i);
	return fState(f);
}
int extState(int s, int p) {
	static int z[maxm];
	for (int i = 0; i < m; ++ i)
		z[i] = gbit(s, i);
	z[p] = z[p - 1];
	z[p - 1] = 0;
	int f(0);
	for (int i = 0; i < m; ++ i)
		f |= mbit(z[i], i);
	return fState(f);
}

int forkState(int s, int p) {
	return fState(s | mbit(7, p));
}

bool legalState(int s, int p) {
	static int z[maxm];
	for (int i = 0; i < m; ++ i)
		z[i] = gbit(s, i);
	for (int i = 0; i < p - 1; ++ i)
		if (z[i])
			return 0;
	for (int i = p + 1; i < m; ++ i)
		if (z[i])
			return 0;
	return 1;
}

void fillArr(int* a, int sz, int v) {
	for (int i = 0; i < sz; ++ i)
		a[i] = v;
}

void dp() {
	int cur(0), prv(1);
	fillArr(f[cur], tots, -inf);
	f[cur][0] = 0;
	for (int x = 0; x < n; ++ x) {
		for (int y = 0; y < m; ++ y) {
			swap(cur, prv);
			fillArr(f[cur], tots, -inf);
			f[cur][0] = 0;
			for (int i = 0; i < tots; ++ i) 
				if (f[prv][i] > -inf) {
					int s(slst[i]);
					if (gbit(s, y) && y && gbit(s, y - 1)) {
						if (gbit(s, y) == gbit(s, y - 1)) {
							if (legalState(s, y))
								upMax(ans, f[prv][i] + a[x][y]);
						}
						else {
							upMax(f[cur][joinState(s, y)], f[prv][i] + a[x][y]);
						}
					}
					if (!gbit(s, y) && y && gbit(s, y - 1)) {
						if (gbit(s, y - 1) == 7)
							upMax(f[cur][expState(s, y)], f[prv][i] + a[x][y]);
						else
							upMax(f[cur][extState(s, y)], f[prv][i] + a[x][y]);
					}
					if (gbit(s, y) && (!y || gbit(s, y - 1) != 7))
						upMax(f[cur][i], f[prv][i] + a[x][y]);
					if (y < m - 1 && !gbit(s, y) && (!y || gbit(s, y - 1) != 7))
						upMax(f[cur][forkState(s, y)], f[prv][i] + a[x][y]);
					if (!gbit(s, y) && (!y || gbit(s, y - 1) != 7))
						upMax(f[cur][i], f[prv][i]);
				}
		}
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &m);
	preState();
	ans = -inf;
	for (int i = 0; i < n; ++ i)
		for (int j = 0; j < m; ++ j)
			scanf("%d", a[i] + j);
	ans = -inf;
	dp();
	printf("%d\n", ans);
}

BZOJ2331 [SCOI2011]地板

插头DP的基础题。想插头DP都是将军没走的时候给我讲的了,距今有大半年了吧。mhy已经写了无数道了,我才开始写。唉,还是效率太低。

 

插头DP的意思是把已经处理的部分的轮廓线上对后面状态有影响的东西状压下来,然后挨着处理每个格子的状态(比如方向,或者这里的地板怎么铺之类的)。

 

具体到这道题来说,考虑一个相邻格子之前的边,那么它可能没有连接两个相同的磁砖,有可能连接了两个还没有拐过弯的磁砖,有可能连接了两个已经拐过弯的磁砖,一共是3种情况,所以用一个三进制数把它压下来。然后直接一路转移过去就行了。要注意的是每行都处理完了之后要把最边上的那一条从右移动到左,所以还需要把所有的状态都改一下。

 

今天效率比较低,心不在焉,然后在最后一个转移的地方wa了大半天。插头DP的调试也比较麻烦。所以我还是naive。

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 13;
const int maxa = 109;
const int maxs = 200009;
const int mod = 20110520;

bool mp[maxa][maxa];
int n, m, a[maxn], b[maxn], f[2][maxs];

#define fc f[cur]
#define fp f[prv]
#define doNothing ;
inline void mInc(int&  _a_, int _b_) {
	_a_ += _b_;
	if (_a_ >= mod)
		_a_ %= mod;
}

void unzipStatus(int z, int* a) {
	for (int i = 0; i <= m; ++ i) {
		a[i] = z % 3;
		z /= 3;
	}
}
int zipStatus(int* a) {
	int s(0);
	for (int i = m; i >= 0; -- i)
		s = s * 3 + a[i];
	return s;
}

int plugDP() {
	int prv(0), cur(1), e(1);
	memset(f, 0, sizeof(f));
	fc[0] = 1;
	for (int i = 0; i <= m; ++ i)
		e *= 3;
	for (int i = 0; i < n; ++ i) {
		for (int j = 0; j < m; ++ j) {
			swap(cur, prv);
			for (int z = 0; z < e; ++ z)
				fc[z] = 0;
			for (int z = 0; z < e; ++ z)
				if (fp[z]) {
					unzipStatus(z, a);
					if (mp[i][j]) {
						if (a[j] == 0) {
							if (a[j + 1] == 0) {
								memcpy(b, a, sizeof(a));
								b[j] = b[j + 1] = 2;
								mInc(fc[zipStatus(b)], fp[z]);
								b[j] = 0;
								b[j + 1] = 1;
								mInc(fc[zipStatus(b)], fp[z]);
								b[j] = 1;
								b[j + 1] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
							}
							else if (a[j + 1] == 1) {
								memcpy(b, a, sizeof(a));
								b[j] = 0;
								b[j + 1] = 2;
								mInc(fc[zipStatus(b)], fp[z]);
								b[j] = 1;
								b[j + 1] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
							}
							else if (a[j + 1] == 2) {
								memcpy(b, a, sizeof(a));
								b[j] = 2;
								b[j + 1] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
								b[j] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
							}
						}
						else if (a[j] == 1) {
							if (a[j + 1] == 0) {
								memcpy(b, a, sizeof(a));
								b[j] = 0;
								b[j + 1] = 1;
								mInc(fc[zipStatus(b)], fp[z]);
								b[j] = 2;
								b[j + 1] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
							}
							else if (a[j + 1] == 1) {
								memcpy(b, a, sizeof(a));
								b[j] = b[j + 1] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
							}
							else if (a[j + 1] == 2) {
								doNothing
							}
						}
						else if (a[j] == 2) {
							if (a[j + 1] == 0) {
								memcpy(b, a, sizeof(a));
								b[j] = b[j + 1] = 0;
								mInc(fc[zipStatus(b)], fp[z]);
								b[j + 1] = 2;
								mInc(fc[zipStatus(b)], fp[z]);
							}
							else if (a[j + 1] == 1) {
								doNothing
							}
							else if (a[j + 1] == 2) {
								doNothing
							}
						}
					}
					else {
						if (a[j] == 0 && a[j + 1] == 0)
							mInc(fc[z], fp[z]);
					}
				}
		}
		swap(cur, prv);
		for (int z = 0; z < e; ++ z) 
			fc[z] = 0;
		for (int z = 0; z < e; ++ z) 
			if (fp[z]) {
				unzipStatus(z, a);
				if (a[m])
					continue;
				for (int j = m; j; -- j)
					a[j] = a[j - 1];
				a[0] = 0;
				fc[zipStatus(a)] = fp[z];
			}
	}
	return fc[0];
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif

	scanf("%d%d", &n, &m);
	bool rev(n < m);
	for (int i = 0; i < n; ++ i) {
		static char g[maxa];
		scanf("%s", g);
		for (int j = 0; j < m; ++ j)
			if (rev)
				mp[j][i] = (g[j] == '_');
			else
				mp[i][j] = (g[j] == '_');
	}
	if (rev)
		swap(n, m);
	printf("%d\n", plugDP());
}