NEFU算法设计与分析课程设计

目录

前言

代码

(一)动态规划问题 2、基因序列相似度

思路:

代码:

(二)回溯法 5、岛屿数量问题

思路:

代码:

(三)分支限界法 1、求源点最短路

思路:

代码:

(四)网络流问题 2、流水问题

思路:

代码:

(补充)(四)网络流问题 3、遍历景点问题

思路:

代码:

结语


前言

本文用于存放个人代码,方便验收时展示,内容仅供参考

代码

(一)动态规划问题 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个查询,复杂度会达到O(Q*mlogn)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 留

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值