目录
前言
本文用于存放个人代码,方便验收时展示,内容仅供参考
代码
(一)动态规划问题 2、基因序列相似度
思路:
易移顶针,鉴定为简单的dp
最长公共子序列类型问题
代码:
#include <bits/stdc++.h>
using namespace std;
map<pair<char, char>, int> table;
void solve() {
int n, m;
string a, b;
cin >> n >> a;
cin >> m >> b;
a = " " + a, b = " " + b;
table[{'A', 'A'}] = 5; table[{'G', 'G'}] = 5; table[{'C', 'C'}] = 5; table[{'T', 'T'}] = 5;
table[{'A', 'C'}] = -1; table[{'A', 'G'}] = -2; table[{'A', 'T'}] = -1; table[{'A', '-'}] = -3;
table[{'C', 'A'}] = -1; table[{'C', 'G'}] = -3; table[{'C', 'T'}] = -2; table[{'C', '-'}] = -4;
table[{'G', 'A'}] = -2; table[{'G', 'C'}] = -3; table[{'G', 'T'}] = -2; table[{'G', '-'}] = -2;
table[{'T', 'A'}] = -1; table[{'T', 'C'}] = -2; table[{'T', 'G'}] = -2; table[{'T', '-'}] = -1;
table[{'-', 'A'}] = -3; table[{'-', 'G'}] = -4; table[{'-', 'G'}] = -2; table[{'-', 'T'}] = -1;
table[{'-', 'C'}] = -4;
vector<vector<int> > dp(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; i++) {
dp[i][0] = dp[i - 1][0] + table[{a[i], '-'}];
}
for (int i = 1; i <= m; i++) {
dp[0][i] = dp[0][i - 1] + table[{'-', b[i]}];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dp[i][j] = max(dp[i - 1][j] + table[{a[i], '-'}], dp[i][j - 1] + table[{'-', b[j]}]);
dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + table[{a[i], b[j]}]);
// cerr << i<<" "<<j<<" "<<dp[i][j]<<endl;
}
}
cout << dp[n][m] << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
(二)回溯法 5、岛屿数量问题
思路:
易移顶针,鉴定为简单的dfs求连通块
每个点出发,如果访问到没访问过的ans++,并且标记该点为访问,反复进行
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<vector<int> > a(n + 1, vector<int>(m + 1, 0)), vis(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
int ans = 0, ok = 0;
array<int, 4> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};
std::function<void(int, int)> dfs = [&](int x, int y) {
if (x < 1 || x > n || y < 1 || y > m || a[x][y] == 0 || vis[x][y]) {
return;
}
vis[x][y] = 1;
ok == 0 ? ok = 1, ans ++ : 1;
for (int i = 0; i < 4; i++) {
dfs(x + dx[i], y + dy[i]);
}
};
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
ok = 0;
dfs(i, j);
}
}
cout << ans << "\n";
return 0;
}
注意,这个有原题:200. 岛屿数量 - 力扣(LeetCode)
leetcode能AC的版本如下:
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int n = grid.size(), m = grid[0].size();
vector<vector<int> > a(n, vector<int>(m, 0)), vis(n, vector<int>(m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
a[i][j] = grid[i][j] - '0';
}
}
int ans = 0;
array<int, 4> dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};
std::function<void(int, int, int)> dfs = [&](int x, int y, int ok) {
if (x < 0 || x >= n || y < 0 || y >= m || grid[x][y] == '0' || vis[x][y]) {
return;
}
vis[x][y] = 1;
ok == 0 ? ok = 1, ans ++ : 1;
for (int i = 0; i < 4; i++) {
dfs(x + dx[i], y + dy[i], ok);
}
};
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
dfs(i, j, 0);
}
}
return ans;
}
};
(三)分支限界法 1、求源点最短路
思路:
题目明确告诉你是最短路问题,每次从源点S出发进行Dijkstra即可(这里我默认他是稀疏图用了堆优化),记得清零。不过他给出的查询数量有点多,如果Q个查询,复杂度会达到m为边的数量,n为点的数量。
这个没有原题,不过有点像Acwing里面那个板子(那个是单查询)
850. Dijkstra求最短路 II - AcWing题库
代码:
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using pii = pair<int, int>;
constexpr int INF = 0x3f3f3f3f;
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<vector<pii> > adj(n);
for (int i = 1; i <= m; i++) {
int x, y, w;
cin >> x >> y >> w;
x --, y --; // cause x, y from 1
adj[x].push_back({w, y});
}
auto dijkstra = [&](int begin, vector<int> &dist) {
vector<bool> visited(n, 0);
dist[begin] = 0;
priority_queue<pii, vector<pii>, greater<pii> > heap; // 小根堆
heap.push({0, begin});
while (heap.size()) {
auto [_, from] = heap.top();
heap.pop();
if (visited[from]) {
continue;
}
for (auto [w, to] : adj[from]) {
if (!visited[to] && dist[from] + w < dist[to]) {
dist[to] = dist[from] + w;
heap.push({dist[to], to});
}
}
}
};
int q;
cin >> q;
while (q --) {
int from, to;
cin >> from >> to;
from --, to --; // 这里别忘记 -1
vector<int> dist(n, INF);
dijkstra(from, dist);
if (dist[to] == INF) { // unachieveable
cout << "No Path!\n";
} else { // achieveable
cout << dist[to] << "\n";
}
}
return 0;
}
/*
一个类似的问题,思想都差不多,堆优化dijkstra
problem url: https://www.acwing.com/problem/content/851/
*/
(四)网络流问题 2、流水问题
思路:
比较经典的最大流问题,可是我不太会,现学的,以下是参考的网上博主的解读:
Dinic 算法: 先用BFS将图划分层次, 然后在用DFS找源点到汇点的增广路径
这类题目大概的套路就是:找一条从起点到终点的路线,(取该路线上权值最小的边,为该次查找的最小的流)然后反转该路线的流向,重复多次直至找不到为止,将每次找到的最小流加起来,就是起点到终点的最大流,也就是问题的解。
这个题用到了Dinic算法和当前弧优化,以后学图论时候来补题
这个有原题,可以在VJ上交POJ的1273 Drainage Ditches - POJ 1273 - Virtual Judge (vjudge.net)
吐槽一下POJ的这个题C++标准居然是C++98???
代码:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int N = 2e2 + 10;
const int INF = 0x3f3f3f3f;
int head[N], depth[N], cur[N];
int n, m, Start, End, cnt;
struct node {
int to, next, w;
}e[N << 1];
void init() {
Start = 1, End = n, cnt = 0;
memset(head, -1, sizeof head);
}
void AddEdge(int u, int v, int w) {
e[cnt].w = w, e[cnt].to = v, e[cnt].next = head[u], head[u] = cnt ++;
e[cnt].w = 0, e[cnt].to = u, e[cnt].next = head[v], head[v] = cnt ++;
}
int dfs(int u, int flow) {
if (u == End) {
return flow;
}
for (int i = cur[u]; ~i; i = e[i].next) {
if (depth[e[i].to] == depth[u] + 1 && (e[i].w != 0)) {
int change = dfs(e[i].to, min(e[i].w, flow));
if (change) {
e[i].w -= change;
e[i ^ 1].w += change;
return change;
}
}
}
return 0;
}
int bfs() {
queue<int> q;
memset(depth, 0, sizeof depth);
q.push(Start);
depth[Start] = 1;
while(q.size()) {
int h = q.front();
q.pop();
for (int i = head[h]; ~i; i = e[i].next) {
if (depth[e[i].to] == 0 && e[i].w > 0) {
depth[e[i].to] = depth[h] + 1;
q.push(e[i].to);
}
}
}
return depth[End] > 0;
}
int dinic() {
int ans = 0;
while (bfs()) {
for (int i = 1; i <= n; i++) {
cur[i] = head[i];
}
while (int d = dfs(Start, INF)) {
ans = ans + d;
}
}
return ans;
}
signed main() {
while (cin >> m >> n) {
init();
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
AddEdge(u, v, w);
}
cout << dinic() << "\n";
}
return 0;
}
/*
problem url: https://vjudge.csgrandeur.cn/problem/POJ-1273
不是很懂。。。
*/
(补充)(四)网络流问题 3、遍历景点问题
思路:
TSP问题,使用Flyod+状压dp来解决,可以参考我之前的博客中的Victor and World。
(109条消息) 状压dp个人刷题记录_CurleyD的博客-CSDN博客
代码:
#include <bits/stdc++.h>
using namespace std;
int f[25][1<<17]; //f[i][j] 表示当前在i节点, 当前状态j的最小权值之和
int g[25][25];
int n, m;
void solve() {
memset(g, 0x3f, sizeof g);
cin >> n >> m;
while (m--) {
int u, v, w;
cin >> u >> v >> w;
u--, v--; //改成从0开始
g[u][v] = g[v][u] = min(g[u][v], w);
}
//Floyd
for(int k = 0; k < n; k++) //
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
//状压dp
memset(f,0x3f,sizeof(f));
f[0][1] = 0;
for (int S = 1; S < (1 << n); S++) //枚举状态
for (int i = 0; i < n; i++) if (S & (1 << i)) //先枚举目标节点
for (int j = 0; j < n; j++) //枚举出发节点
if (S & (1 << j) && g[j][i])
f[i][S] = min(f[i][S], f[j][S ^ (1 << i)] + g[j][i]);
//找到最小的
for(int i = 1; i < n; i++)
f[0][(1 << n) - 1] = min(f[0][(1 << n) - 1], f[i][(1 << n) - 1] + g[i][0]);
cout << f[0][(1 << n) - 1] << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
结语
实话说这学期算法的成绩估计不会很好,考试那些题目都很常规,稍微学学就能很高分。拿不了好的分数,我自己的原因占大多数吧。
争取踏实下来去学习,不能总是飘在天上。 ——2023.6.15 22:34 留