(一)动态规划问题
最大子矩阵问题
【题目描述】
有一个包含正数和负数的二维数组。一个子矩阵是指在该二维数据里,任意相邻的下标是1 * 1或更大的子数组。一个子矩阵的和是指该子矩阵中所有元素的和。本题中,把具有最大和的子矩阵称为最大子矩阵。
例如,如下数组的最大子矩阵位于左下角,其和为15。
|----|----|----|----|
| 0 | -2 | -7 | 0 |
| 9 | 2 | -6 | 2 |
| -4 | 1 | -4 | 1 |
| -1 | 8 | 0 | -2 |
【输入】
是N * N个整数的数组。第一行是一个正整数N,表示二维方阵的大小。接下来是N2个整数(空格和换行隔开)。该数组的N2个整数,是以行序给出的。也就是,显示第一行的数,由左到右;然后是第二行的数,由左到右,等等。
N可能达到100,数组元素的范围是[-127,127]
【输出】
最大子矩阵的和。
【输入样例】
4
0 -2 -7 0 9 2 -6 2
-4 1 -4 1 -1 8 0 -2
【输出样例】
15
cpp
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
//f 是求一维数组最大子数组和的函数
//g 是求二维矩阵最大子矩阵和的函数
//a 是输入的二维矩阵
//n 是矩阵的大小
//l 和 r 分别是左边界和右边界
//t 是累加和的一维数组
//m 是当前最大的子数组和或子矩阵和
//c 是当前累加的子数组和
int f(vector<int>& a) {
int m = INT_MIN, c = 0;
for (int x : a) {
c = max(x, c + x);//对于每个元素,计算在当前位置结束的最大子数组和
m = max(m, c);//更新全局最大子数组和
}
return m;
}
int g(vector<vector<int>>& a) {
int n = a.size();
int m = INT_MIN;
for (int l = 0; l < n; ++l) {
vector<int> t(n, 0);
for (int r = l; r < n; ++r) {
for (int i = 0; i < n; ++i) {
t[i] += a[r][i];
}
int c = f(t);
m = max(m, c);
}
}
return m;
}
int main() {
int n;
cin >> n;
vector<vector<int>> a(n, vector<int>(n));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cin >> a[i][j];
}
}
cout << g(a) << endl;
return 0;
}
(二)回溯法
括号生成问题
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。例如,给出n = 3,生成结果为:["((()))", "(()())", "(())()", "()(())", "()()()"]。
cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
//O是已使用的左括号的数量
//p是已使用的右括号的数量
//r是结果
void backtrack(vector<string> &r, string &c, int o, int p, int m) {
if (c.size() == m * 2) {
r.push_back(c);
return;
}
if (o < m) {
c.push_back('(');
backtrack(r, c, o + 1, p, m);
c.pop_back();
}
if (p < o) {
c.push_back(')');
backtrack(r, c, o, p + 1, m);
c.pop_back();
}
}
vector<string> g(int n) {
vector<string> r;
string c;
backtrack(r, c, 0, 0, n);
return r;
}
int main() {
int n;
cin >> n;
vector<string> r = g(n);
for (const string &s : r) {
cout << s << endl;
}
return 0;
}
(三)分支限界法
求解饥饿的小易问题
问题描述:
小易总是感到饥饿,所以作为章鱼的小易需要经常出去找贝壳吃。最开始,小易在一个初始位置x0.对于小易所处的当时位置x,它智能通过神秘的力量移动4 * x + 3或者8 * x +7。因为使用神秘力量要消耗太多体力,所以它最多只能使用神秘力量100000次。贝壳总生长在能被1000000007整除的位置(比如位置0、位置1000000007、位置2000000014等)。小易需要你帮忙计算最少使用多少次神秘力量就能吃到贝壳。
输入描述:
输入一个初始位置x0,范围为1-1000000006
输出描述:
输出小易最少需要使用神秘力量的次数,如果次数使用完还没找到贝壳,则输出-1.
输入样例
125000000
输出样例
1
cpp
#include <iostream>
#include <map>
#include <queue>
using namespace std;
#define MOD 1000000007L
#define MAX 100000
//vist记录每个位置及其到起始位置的距离或使用力量次数
int main(){
map<long,int> vist;
long x;//存储初始位置
while (cin>>x) {
queue<long> q;
q.push(x);
vist[x]=1;
long xx=1;
while (q.size()) {
x = q.front();
q.pop();
if (x==0)//找到贝壳
break;
if (vist[x] > MAX)
continue;
xx = ((x<<2)+3) % MOD;
if (vist.find(xx) == vist.end())//新位置未被访问过
{
q.push(xx);
vist[xx] = vist[x]+1;
}
xx = ((x<<3)+7) % MOD;
if (vist.find(xx) == vist.end()){
q.push(xx);
vist[xx] = vist[x]+1;
}
}
cout<<(q.size() ? vist[x]-1 : -1)<<endl;
}
return 0;
}
(四)网络流问题
流水问题
现在有m个池塘(从1到m开始编号,1为源点,m为汇点)及n条水渠。假设已经给出这n条水渠所连接的池塘和所能流过的水量,设计算法求水渠中所能流过的水的最大容量。示例如下:
输入:
4 5 //池塘数m和水渠数n
1 2 40 //所连接的池塘和所能流过的水量
1 4 20
2 4 20
2 3 30
3 4 10
输出:50 //最大流水量
cpp
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
//a代表邻接表
//e代表边
//adEdge代表添加边
//mF代表最大流函数
//f是累计的最大流量
const int INF = 1e9;
//f是边的起点
//t是边的终点
//c是边的容量
//f1是边的流量
struct Edge {
int f, t, c, fl;
};
vector<vector<int>> a;
vector<Edge> e;
void adEdge(int f, int t, int c) {
Edge e1 = {f, t, c, 0};
Edge e2 = {t, f, c, 0};
a[f].push_back(e.size());
e.push_back(e1);
a[t].push_back(e.size());
e.push_back(e2);
}
//s是源点,k是汇点
//p数组用来记录路径
int mF(int s, int k) {
int f = 0;
while (true) {
vector<int> p(a.size(), -1);
queue<int> q;
q.push(s);
p[s] = s;//源点已被访问
while (!q.empty() && p[k] == -1) {
int u = q.front();
q.pop();
for (int i : a[u]) {
Edge &x = e[i];
if (p[x.t] == -1 && x.c - x.fl > 0) {
q.push(x.t);
p[x.t] = i;
}
}
}
if (p[k] == -1) break;
int cF = INF;//表示路径上最小的剩余容量
for (int u = k; u != s; u = e[p[u]].f) {
cF = min(cF, e[p[u]].c - e[p[u]].fl);
}
for (int u = k; u != s; u = e[p[u]].f) {
e[p[u]].fl += cF;//将路径上正向边的流量增加cF
e[p[u]^1].fl -= cF;//将路径上反向边的流量减少cF
}
f += cF;
}
return f;
}
int main() {
int m, n;
cin >> m >> n;
a.resize(m + 1);
for (int i = 0; i < n; ++i) {
int f, t, c;
cin >> f >> t >> c;
adEdge(f, t, c);
}
int s = 1, k = m;
int mf = mF(s, k);
cout << mf << endl;
return 0;
}