codeforecs1391D (#663 Div2) 505

题目链接:
    https://codeforces.com/contest/1395/problem/D
题目大意:
    先给出一个n, 一个m,保证n*m<= 1e6
    然后给出n行m列的一个矩阵(只包含数字0和1)
    先要求你更改一些位置(可以不更改)
    使得改矩阵中每个变成为偶数的方形区域内数字1的个数为奇数
样例输入:
第一组:

3 3
101
001
110

第二组:

7 15
000100001010010
100111010110001
101101111100100
010000111111010
111010010100001
000011001111101
111111011010011

样例输出:
第一组:

2

第二组:

-1

题解:

1.如图1,可以证明其实min(n, m)<= 3 最终才可能有解图1
2.如图2,对于min(n,m)<= 3时,我们让n <= m(矩形横竖摆放答案都一样),对于枚举到第i位,
我们可以,枚举第i为的状态(s),再枚举第i-1位的状态为(ps),(n很小s最多8种,ps也是)
然后保证红框内的1的个数都为奇数个。便可以使得dp[i][s] 从dp[i-1][ps]更新过来。
图2

dp[i][s] = dp[i - 1][ps] + (本次花费)

ac代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 1e6 + 5;

int dp[maxn][8];
char tmp[maxn];
vector<vector<bool>>vec(maxn);
int n, m;

int getTake(int ii, int st);
bool chkValid(int pst, int st);
void Min(int &xx, int yy);
void out_dp();

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%s", tmp);
		for (int j = 0; j < m; j++)
			vec[i].push_back(tmp[j] - '0');
	}

	if (n >= 4 && m >= 4)
		printf("-1\n");
	else {
		if (n > m)
			swap(n, m);
		if (n == 1) printf("0\n");
		else {
			int maxSt = 1 << n;

			memset(dp, -1, sizeof(dp));
			for (int st = 0; st < maxSt; st++)
				dp[1][st] = getTake(1, st);

			for (int ii = 2; ii <= m; ii++) {
				for (int st = 0; st < maxSt; st++) {
					int thisTake = getTake(ii, st);

					for (int pst = 0; pst < maxSt; pst++) {
						if (dp[ii - 1][pst] == -1) continue;
						if (!chkValid(pst, st)) continue;
						Min(dp[ii][st], dp[ii - 1][pst] + thisTake);
						//printf("dp[%d][%d] from pst=%d get:%d\n", ii, st, pst, dp[ii][st]);
					}
				}
			}
			int res = -1;

			for (int st = 0; st < maxSt; st++) Min(res, dp[m][st]);
			printf("%d\n", res);

			//out_dp();
		}
	}
	return 0;
}

int getTake(int ii, int st)
{
	int res = 0;

	for (int i = 1; i <= n; st >>= 1, i++)
		if ((st & 1) != vec[i][ii - 1])
			res++;

	return res;
}

int get1(int xx)
{
	int res = 0;

	while (xx) {
		res += xx & 1;
		xx >>= 1;
	}
	return res;
}

bool chkValid(int pst, int st)
{
	for (int i = 2, j = 3; i <= n; i++, j <<= 1) {
		int pthis2 = pst & j;
		int this2 = st & j;
		
		if (!((get1(pthis2) + get1(this2)) & 1)) return 0;
	}
	return 1;
}

void Min(int & xx, int yy)
{
	if (xx == -1 || yy < xx)
		xx = yy;
}

void out_dp()
{
	int maxSt = 1 << n;

	for (int i = 1; i <= m; i++)
		for (int st = 0; st < maxSt; st++)
			printf("dp[%d][%d]=%d\n", i, st, dp[i][st]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值