最简单的一道题,枚举所有区间[a,b],把[a,b]外最大的数与[a,b]中最小的数交换(最多交换k个),最后取最大的值
首先要明白,最后满足条件的矩阵一定满足如下条件:
存在一个基准行(或列),其余各行与它要么相同,要么完全相反。比如取第一行为基准行,对其余各行用数学归纳法就可以证明了。
剩下的工作就是要找到这样的基准行,方法很简单——枚举所有可能的基准行。如果有m列那么就有2^m种可能的基准行,m最大取100,肯定会超时。注意到k≤10,而且答案不超过k,考虑如下情况:
当m>k时,如果基准列(注意这里以列为基准)不同于原来的任何一列(也不会和它完全相反,否则把基准列换为它),那么每一列都至少要变换一个数,也就是至少变换m>k个数,矛盾。于是在这种情况下基准列只能是原来m列中的某一列。
最后总结如下:
if (m > k) {
//枚举每一列作为基准列
for (int i = 0; i < m; i++) {
//code here
}
}
else {
//枚举所有可能的基准行
//不超过2^m(m <= k <= 10)种
//可以用二进制表示
for (int i = 0; i < (1 << m); i++) {
//code here
}
}
完整代码如下:
#include
#include
#include
using namespace std;
const int MAX_SIZE = 100 + 2;
const int INF = 100;
int a[MAX_SIZE][MAX_SIZE];
int main(int argc, char *argv[])
{
// freopen("D:\\in.txt", "r", stdin);
int n, m, k, i, j;
cin >> n >> m >> k;
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
cin >> a[i][j];
}
}
int ans = INF;
if (m > k) {
for (int base_col = 0; base_col < m; base_col++) {
int tmp = 0; //candidate answer
for (j = 0; j < m; j++) {
int cnt = 0; //the number of different cells in column j
for (i = 0; i < n; i++) {
cnt += (a[i][j] != a[i][base_col]);
}
tmp += min(cnt, n - cnt);
}
ans = min(ans, tmp);
}
}
else {
for (int base_row = 0; base_row < (1 << m); base_row++) {
int tmp = 0;
for (i = 0; i < n; i++) {
int cnt = 0;
for (j = 0; j < m; j++) {
cnt += ((base_row >> j) & 1) == a[i][j];
}
tmp += min(cnt, m - cnt);
}
ans = min(ans, tmp);
}
}
if (ans > k) {
ans = -1;
}
cout << ans << endl;
return 0;
}
动态规划题目,多一个判断条件而已。
用dp[i][j]表示第j次执行1操作且删除的是a[i]时数列b删除的数的最小下标(即此时数列b删除b[dp[i][j]])。如果数列b中没有这样的数,那么dp[i][j] = INF。递推关系为:记
f(k) = min{x | x > dp[k][j - 1] && b[x] == a[i]}
即第j - 1次删除a[k],第j次删除a[i]时数列b删除的数的最小下标,则
dp[i][j] = min{f(k)}(0 ≤ k < i)
最后判断满足能量关系就行了。
完整代码如下:
#include
#include
#include
#include
#include
#include
using namespace std; const int MAX_SIZE = 100000 + 2; int a[MAX_SIZE], b[MAX_SIZE]; int dp[MAX_SIZE]; vector
pos[MAX_SIZE]; int main(int argc, char *argv[]) { // freopen("D:\\in.txt", "r", stdin); int n, m, s, e, i, j; cin >> n >> m >> s >> e; for (i = 0; i < n; ++i) { scanf("%d", a + i); --a[i]; } for (i = 0; i < m; ++i) { scanf("%d", b + i); --b[i]; pos[b[i]].push_back(i); } for (i = 0; i < MAX_SIZE; i++) { pos[i].push_back(INT_MAX); } int cnt = s / e; int ans = 0; dp[0] = -1; for (i = 1; i <= cnt; ++i) { dp[i] = INT_MAX - 1; } for (i = 0; i < n; ++i) { for (j = cnt; j; --j) { dp[j] = min(dp[j], *upper_bound(pos[a[i]].begin(), pos[a[i]].end(), dp[j - 1])); if (dp[j] <= s - i - 2 - j * e && j > ans) { ans = j; } } } cout << ans << endl; return 0; }
用collx[i]存储x坐标为i的所有点的y坐标,用colly[i]存储y坐标为i的所有点的x坐标(相当于collx[i]表示直线x = i)。对于点P(x, y),搜寻以P为左下顶点的正方形,遍历直线collx[x]和colly[y]上的点,如果满足边长关系,再看是否存在第四个顶点(右上顶点)。
完整代码如下:
#include
#include
#include
#include
#include
#include
using namespace std; const int MAX_N = 100000 + 2; vector
collx[MAX_N], colly[MAX_N]; vector
::iterator pos1, pos2, pos3; int main(int argc, char *argv[]) { int n, i, j; cin >> n; int maxx = 0, maxy = 0; for (i = 0; i < n; i++) { int x, y; scanf("%d%d", &x, &y); collx[x].push_back(y); colly[y].push_back(x); maxx = max(maxx, x); maxy = max(maxy, y); } for (i = 0; i <= maxx; i++) { sort(collx[i].begin(), collx[i].end()); } for (i = 0; i <= maxy; i++) { sort(colly[i].begin(), colly[i].end()); } int cnt = 0; for (i = 0; i <= maxx; i++) { for (pos1 = collx[i].begin(); pos1 != collx[i].end(); ++pos1) { int x = i, y = *pos1; pos2 = lower_bound(colly[y].begin(), colly[y].end(), x); pos3 = pos1 + 1; ++pos2; while (pos2 != colly[y].end() && pos3 != collx[x].end()) { if (*pos2 - x < *pos3 - y) { ++pos2; } else if (*pos2 - x > *pos3 - y) { ++pos3; } else { if (binary_search(collx[*pos2].begin(), collx[*pos2].end(), *pos3)) { ++cnt; } ++pos2; ++pos3; } } } } cout << cnt << endl; return 0; }
还在研究中。。。