Codeforces Round #243 (Div. 2)——Sereja and Table

看这个问题之前,能够先看看这个论文《一类算法复合的方法》,说白了就是分类讨论,可是这个思想非常重要

题目链接

  • 题意:

    首先给出联通块的定义:对于相邻(上下和左右)的同样的数字视为一个联通块

    现给一个n*m的仅仅有0和1的矩形和数字k,求出最小反转个数使得总体包含若干个矩形联通块(即每一个联通块均是矩形)(1?≤?n,?m?≤?100; 1?≤?k?≤?10)

    假设最小次数比k大,输出-1

  • 分析:

    题目的特点是k比較小。也就是说反转的次数比較少,所以能够从这里入手。直接枚举全部的位置肯定是不行了,那么能够这样考虑:(最好还是设n>=m)假设n比k大,那么肯定有一些行是不会有反转的数字的,那么我们能够枚举每一行来处理;假设k比n大,这个时候n小于10,所以这时候我们就能够暴力枚举每一行的全部状态。然后处理。

    以上两种方法处理的时候均根据下边的图形特点,仅仅知道一行的时候就能够求出最小的总反转数

终于仅仅能是

01010...

10101...

...

的形状(当中一个字符代表一个矩形)

const int MAXN = 110;

int ipt[MAXN][MAXN];

int main()
{
//    freopen("in.txt", "r", stdin);
	int n, m, k;
	while (~RIII(n, m, k))
	{
		REP(i, n) REP(j, m) RI(ipt[i][j]);
		if (n < m)
		{
			REP(i, n) FF(j, i + 1, m) swap(ipt[i][j], ipt[j][i]);
			swap(n, m);
		}
		if (n > k)
		{
			int ans = INF;
			REP(i, n)
			{
				int tans = 0;
				REP(j, n)
				{
					int cnt = 0;
					if (i == j) continue;
					REP(k, m)
					{
						if (ipt[i][k] != ipt[j][k]) cnt++;
					}
					tans += min(cnt, m - cnt);
				}
				ans = min(ans, tans);
			}
			printf("%d\n", ans <= k ? ans: -1);
		}
		else
		{
			int ans = INF;
			REP(i, n)
			{
				int all = 1 << m;
				for (int q = 0; q < all; q++)
				{
					int diff = 0;
					for (int t = 0, l = 1; t < m; l <<= 1, t++) if (((q & l) != 0) != ipt[i][t]) diff++;
					if (diff > k) continue;
					int tans = 0;
					REP(j, n)
					{
						if (i == j) continue;
						int cnt = 0;
						for (int t = 0, l = 1; t < m; t++, l <<= 1) if (((q & l) != 0) != ipt[j][t]) cnt++;
						tans += min(cnt, m - cnt);
					}
					ans = min(ans, diff + tans);
				}
			}
			printf("%d\n", ans <= k ? ans: -1);
		}
	}
	return 0;
}

參照大神的代码后的一些细节改动:

const int MAXN = 110;

int ipt[MAXN][MAXN];

int main()
{
//    freopen("in.txt", "r", stdin);
	int n, m, k;
	while (~RIII(n, m, k))
	{
		int ans = INF, all = 1 << m;
		REP(i, n) REP(j, m) RI(ipt[i][j]);
		if (n < m)
		{
			REP(i, n) FF(j, i + 1, m) swap(ipt[i][j], ipt[j][i]);
			swap(n, m);
		}
		if (n > k)
		{
			REP(i, n)
			{
				int tans = 0;
				REP(j, n)
				{
					int cnt = 0;
					REP(k, m)
						cnt += ipt[i][k] ^ ipt[j][k];
					tans += min(cnt, m - cnt);
				}
				ans = min(ans, tans);
			}
		}
		else
		{
			for (int mask = 0; mask < all; mask++)
			{
				int tans = 0;
				REP(i, n)
				{
					int cnt = 0;
					REP(j, m) cnt += ipt[i][j] ^ (mask >> j & 1);
					tans += min(cnt, m - cnt);
				}
				ans = min(ans, tans);
			}
		}
		printf("%d\n", ans <= k ? ans: -1);
	}
	return 0;
}
时间: 2024-10-18 08:08:31

Codeforces Round #243 (Div. 2)——Sereja and Table的相关文章

Codeforces Round #243 (Div. 1)——Sereja and Two Sequences

题目链接 题意:给两个长度分别为n和m的序列,现在有两种操作:1.分别选择两个序列的一个非空前缀,切两个前缀的最后一位相同,删除之,得到1分(只累计),消耗e:2.直接删除两个序列,消耗值定于两个序列之前删除的元素个数之和,并且使得得到的分有效(之前没有有效分) 分析: 首先,问题其实就是转化成,进行若干次操作1,然后进行操作2 还要找到一个判别标准,来评判较优的状态(贪心) 每次的消耗值比较大,其实可以计算出最大的删除次数,这个值不是很大 状态表示: 简单的,一个状态可以表示为串A的位置.串B

Codeforces Round #243 (Div. 1)——Sereja and Squares

题目链接 题意: 给n个点,求能组成的正方形的个数. 四边均平行与坐标轴 大神的分析: 经典题 我们考虑每一种x坐标,显然仅仅有<= sqrt{N}个x坐标出现了> sqrt{N}次,我们称这些为大的,其它为小的. 我们先考虑大的x和其它x之间的答案,先O(sqrt{N})枚举一个大的坐标,然后for其它的每一个点,这样能够依据x坐标的差算出正方形的边长,hash检查一下就能知道这个正方形是否存在. 之后考虑小的x和小的x之间的答案,注意到我们能够对每一个横坐标直接平方for,这样仅仅有(sq

Codeforces Round #243 (Div. 2)——Sereja and Swaps

题目链接 题意: 给定一个整数序列长度为n,可以至多交换k次,求最大连续区间和(1?≤?n?≤?200; 1?≤?k?≤?10) 分析: 自己上来先考虑的方向是:先找出最大连续区间和,然后逐个交换,但是这样没法处理.对于最大区间内的交换直接找出最小值即可,但是如果最优位置不在当前区间内,情况就不好处理了 根据上述特点,方向应该是,固定区间长度,然后进行交换.这样的复杂度是O(n^3),对于数据可以接受 const int MAXN = 210; int ipt[MAXN], ta[MAXN],

Codeforces Round #243 (Div. 2) A. Sereja and Mugs

#include <iostream> #include <vector> #include <algorithm> #include <numeric> using namespace std; int main(){ int n,s; cin >> n >> s; vector<int> a(n); for(int i = 0 ; i < n ; ++ i) cin >> a[i]; sort(a.b

Codeforces Round #243 (Div. 2) B. Sereja and Mirroring

#include <iostream> #include <vector> #include <algorithm> using namespace std; int main(){ int n,m; cin >> n >> m; vector<vector<int> > a(n,vector<int>(m,0)); for(int i = 0; i < n; ++ i){ for(int j = 0 ;

Codeforces Round #243 (Div. 2) C. Sereja and Swaps

思路来源:http://blog.csdn.net/sf____/article/details/24626739 题目给出数据上限为200, 所以可以暴利所有区间. 解题思路: for i in range(n): for j in range(n): create priority_queue for w in range(k): if(Max.top > Min.top) SWAP(Max[element], Min[element]) 暴利枚举所有初始区间 [ i , j ] ,则剩下的

Codeforces Round #243 (Div. 2) C. Sereja and Swaps(优先队列 暴力)

题目 题意:求任意连续序列的最大值,这个连续序列可以和其他的 值交换k次,求最大值 思路:暴力枚举所有的连续序列.没做对是因为 首先没有认真读题,没看清交换,然后,以为是dp或者贪心 用了一下贪心,各种bug不对. 这次用了一下优先队列,以前用的不多,看这个博客又学了一下 AC代码: 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #i

Codeforces Round #243 (Div. 1)

---恢复内容开始--- A 枚举l,r 1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 us

Codeforces Round #243 (Div. 1) A题

http://codeforces.com/contest/425/problem/A 题目链接: 然后拿出这道题目是很多人不会分析题目,被题目吓坏了,其中包括我自己,想出复杂度,一下就出了啊!真是弱! 直接暴力求出矩阵数值,然后枚举每一个[I,J];再O[N]判断,分配好在[I,J]区间的数和之内的数,再排序下SOLO了 CODE:#include <cstdio> #include <cstring>#include <queue>#include <vect