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

(一)动态规划问题

最大子矩阵问题

【题目描述】

有一个包含正数和负数的二维数组。一个子矩阵是指在该二维数据里,任意相邻的下标是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

#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,生成结果为:["((()))", "(()())", "(())()", "()(())", "()()()"]。

#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

#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    //最大流水量

#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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值