先枚举行,再枚举列。枚举行的方法为二进制枚举法,为了避免遗漏掉情况,所以假定如果为一,则选取该行,并修改该行的所有数字为零,否则不选取。二进制枚举法即枚举从所有二进制为零到所有二进制为一的情况,这样可以避免遗漏。同时如果当前选取的行数大于目标操作数,则直接重新开始下一次选取。选取好行之后,再按照每列和的从大到小的顺序依次选取。
#include <bits/stdc++.h>
using namespace std;
int num1[20][20];
int num2[20][20];
bool cmp(long long a, long long b) {
return a > b;
}
int main() {
int n, m, k;
cin >> n >> m >> k;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
cin >> num1[i][j];
num2[i][j] = num1[i][j];
}
}
if(n <= k || m <= k) {
long long res = 0;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
res = res + num1[i][j];
cout << res << endl;
} else {
long long res = INT_MIN;
for(int i = 0; i <= (1<<n) - 1; i++) {
long long ans = 0;
int select = 0, tmp = i, start = 0;
while(tmp) {
if(tmp % 2 == 1) {
select += 1;
for(int j = 0; j < m; j++) {
ans = ans + num1[start][j];
num1[start][j] = 0;
}
}
tmp /= 2;
start += 1;
}
if(select > k) {
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
num1[i][j] = num2[i][j];
continue;
} else if(select == k) {
res = max(res, ans);
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
num1[i][j] = num2[i][j];
continue;
} else {
vector<long long> tmpsum;
for(int i = 0; i < m; i++) {
long long tmpsum1 = 0;
for(int j = 0; j < n; j++) {
tmpsum1 += num1[j][i];
}
tmpsum.emplace_back(tmpsum1);
}
sort(tmpsum.begin(), tmpsum.end(), cmp);
for(int i = 0; i < k - select; i++)
ans = ans + tmpsum[i];
res = max(res, ans);
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
num1[i][j] = num2[i][j];
}
}
cout << res << endl;
}
return 0;
}
贪心法。先按照开头序号从小到大排序,如果开头序号相同,按照结束序号从大到小排序。之后对排序结果进行遍历,如果第一个结果的开始序号不为一,直接输出-1即可。在遍历过程中,每次更新结束时间的最大值,如果到达n,则直接输出结果结束即可。如果结束时间最大值在这一次循环中没有更新,说明无法遍历到n,直接输出-1即可。
#include <bits/stdc++.h>
using namespace std;
bool cmp(pair<long long,long long>& a, pair<long long,long long>& b) {
if(a.first == b.first)
return a.second > b.second;
return a.first < b.first;
}
int main() {
long long n, m;
cin >> n >> m;
vector<pair<long long,long long>> p;
for(int i = 0; i < m; i++) {
long long l, r;
cin >> l >> r;
pair<long long,long long> tmp;
tmp.first = l, tmp.second = r;
p.emplace_back(tmp);
}
sort(p.begin(), p.end(), cmp);
long long start = p[0].first, end = p[0].second;
long long res = 1, idx = 1;
if(start != 1) {
cout << -1 << endl;
return 0;
}
while(idx < p.size()) {
long long tmpe = end;
if(end == n)
break;
while(idx < p.size()) {
if(p[idx].first <= end + 1)
tmpe = max(tmpe, p[idx].second);
else
break;
idx += 1;
}
if(tmpe == end) {
cout << -1 << endl;
return 0;
}
res += 1;
end = tmpe;
if(end == n)
break;
}
cout << res << endl;
return 0;
}
贪心法。分成三类,稳定值增加,不变,减少。首先肯定是增加的,其次不变的,最后减少的。增加的和不变的需要按照稳定值消耗值从小到大的顺序遍历,最后减少的贪心策略需要进行改变。如果只是按照之前的贪心策略,很容易举出反例。考虑从后往前遍历减少的情况,可以发现最后一个要符号条件,需要当前值大于消耗值,而恢复值是不需要看的,因为消耗完就结束了,所以恢复值不是那么重要。那么这样考虑来看,最好是给最后一个的恢复值最小,依次法,再考虑倒数第二个,同理可得。因此可以倒出减少的贪心策略应该是,按照恢复值从大到小排序即可进行遍历。
#include <bits/stdc++.h>
using namespace std;
bool cmp1(pair<long long,long long>& a, pair<long long,long long>& b) {
return a.first < b.first;
}
bool cmp2(pair<long long,long long>& a, pair<long long,long long>& b) {
return a.second > b.second;
}
int main() {
int t;
cin >> t;
while(t--) {
int n;
long long m;
cin >> n >> m;
vector<pair<long long,long long>> smaller;
vector<pair<long long,long long>> bigger;
vector<pair<long long,long long>> equals;
smaller.clear();
bigger.clear();
equals.clear();
for(int i = 0; i < n; i++) {
long long a, b;
cin >> a >> b;
pair<long long,long long> tmp;
tmp.first = a, tmp.second = b - a;
if(b > a)
bigger.emplace_back(tmp);
else if(b == a)
equals.emplace_back(tmp);
else {
tmp.second = b;
smaller.emplace_back(tmp);
}
}
sort(bigger.begin(), bigger.end(), cmp1);
bool flag = true;
for(int i = 0; i < bigger.size(); i++) {
if(m >= bigger[i].first)
m += bigger[i].second;
else {
flag = false;
break;
}
}
if(!flag) {
cout << "No\n";
continue;
}
sort(equals.begin(), equals.end(), cmp1);
for(int i = 0; i < equals.size(); i++) {
if(m >= equals[i].first)
m += equals[i].second;
else {
flag = false;
break;
}
}
if(!flag) {
cout << "No\n";
continue;
}
sort(smaller.begin(), smaller.end(), cmp2);
for(int i = 0; i < smaller.size(); i++) {
if(m >= smaller[i].first)
m -= smaller[i].first, m += smaller[i].second;
else {
flag = false;
break;
}
}
if(m < 0)
flag = false;
if(!flag) {
cout << "No\n";
continue;
} else
cout << "Yes\n";
}
return 0;
}
看到异或操作其实第一反应就是将数据改写成二进制形式,之后再观察样例的输出结果,可以发现如果当前二进制位零的个数超过了数据个数的一半,就讲结果这一位二进制写为零,否则写为一(因为需要多组可行解的情况下,数值尽可能小)(零的个数正好为一半时可以为零,也可以为一,但是为了数值尽可能小,所以选择为零)但是直接遍历只能过60%,还需要优化。考虑到区间问题,可以使用前缀和进行优化,这样对于区间求解可以直接一步到位求解。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10;
int presum[MAXN][32] = {0};
int main() {
int n;
cin >> n;
for(int i = 1; i <= n; i++) {
long long a;
cin >> a;
for(int j = 0; j <= 30; j++, a /= 2)
presum[i][j] = presum[i - 1][j] + a % 2;
}
int q;
cin >> q;
for(int i = 0; i < q; i++) {
int l, r;
cin >> l >> r;
long long res = pow(2, 31) - 1, sum = 1;
for(int j = 0; j < 31; j++, sum *= 2) {
int one = presum[r][j] - presum[l - 1][j];
if(one * 2 >= (r - l + 1))
res = res - sum;
}
cout << res << endl;
}
return 0;
}
本文探讨了矩阵消除游戏的解决方案,采用二进制枚举法选取行和列,确保全面覆盖所有可能。接着分析了贪心法在时间排序问题中的应用,按特定顺序遍历以寻找有效序列。最后,介绍了在处理变化值问题时的贪心策略,通过调整遍历顺序以达到最优解。文章通过具体例子展示了如何优化异或操作问题,利用前缀和提高效率。

248

被折叠的 条评论
为什么被折叠?



