1. 神经网络

来源:NOIP2003提高组 https://ac.nowcoder.com/acm/contest/251/A

算法知识点: 拓扑排序

复杂度:

解题思路:

这道题目需要注意输入层的初始状态不用减去阈值。

为了保证使用每个点的状态去更新其他点时,该点的状态已被计算完毕,我们需要使用拓扑序来计算每个点的值。

计算完拓扑序列后,我们只需从前往后递推一遍,即可求出每个点的最终状态值。

C++ 代码:

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std; const int N = 110,
    M = N *N / 2;

int n, m;
int h[N], e[M], w[M], ne[M], idx;
int f[N], u[N], din[N], dout[N];
int q[N];

void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

void topsort()
{
    int hh = 0, tt = -1;
    for (int i = 1; i <= n; i++)
        if (!din[i])
            q[++tt] = i;

    while (hh <= tt)
    {
        int t = q[hh++];
        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if (--din[j] == 0)
                q[++tt] = j;
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d%d", &f[i], &u[i]);
        if (!f[i]) f[i] -= u[i];
    }
    memset(h, -1, sizeof h);

    while (m--)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
        dout[a]++, din[b]++;
    }

    topsort();

    for (int i = 0; i < n; i++)
    {
        int j = q[i];

        if (f[j] > 0)
        {
            for (int k = h[j]; ~k; k = ne[k])
                f[e[k]] += f[j] *w[k];
        }
    }

    bool flag = true;
    for (int i = 1; i <= n; i++)
        if (!dout[i] && f[i] > 0)
        {
            printf("%d %d\n", i, f[i]);
            flag = false;
        }

    if (flag) puts("NULL");

    return 0;
}

 

2. 双栈排序

来源:NOIP2008提高组 https://ac.nowcoder.com/acm/contest/256/D

算法知识点: 二分图,栈,染色法,贪心

复杂度:

解题思路:

如果只有一个栈,则整个操作顺序是固定的:

  • 从前往后遍历每个数,每次先将当前数压入栈中,如果后面的所有数均比栈顶元素大,则将栈顶弹出,否则栈顶不能被弹出。

因此,我们只需考虑将每个数分配给哪个栈即可。

这里有个很强的性质:

两个数 不能被放入同一个栈中,当且仅当存在 , 且


有了上述性质后,我们只需将所有满足条件的点分到两个栈中去即可。这一步可以转化成图论问题:

  • 如果i, j满足条件,则在i和j之间连一条边。

然后判断是否是二分图即可。

答案要求字典序最小,因此从前往后染色时,需要优先将当前点分配到第一个栈中。

C++ 代码:

#include <iostream>
#include <algorithm>
#include <stack>
#include <cstring>
using namespace std; const int N = 1010;

int n;
int a[N], f[N];
int color[N];
bool g[N][N];

bool dfs(int u, int c)
{
    color[u] = c;
    for (int i = 1; i <= n; i++)
        if (g[u][i])
        {
            if (color[i] == c) return false;
            if (color[i] == -1 && !dfs(i, !c)) return false;
        }

    return true;
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        f[n + 1] = n + 1;
        memset(g, false, sizeof g);
        for (int i = n; i; i--) f[i] = min(f[i + 1], a[i]);

        for (int i = 1; i <= n; i++)
            for (int j = i + 1; j <= n; j++)
                if (a[i] < a[j] && f[j + 1] < a[i])
                    g[i][j] = g[j][i] = true;

        memset(color, -1, sizeof color);

        bool flag = true;
        for (int i = 1; i <= n; i++)
            if (color[i] == -1 && !dfs(i, 0))
            {
                flag = false;
                break;
            }

        if (!flag)
        {
            cout << 0 << endl;
            continue;
        }

        stack<int> stk1, stk2;

        int now = 1;
        for (int i = 1; i <= n; i++)
        {
            if (color[i] == 0)
            {
                stk1.push(a[i]);
                cout << "a ";
            }
            else
            {
                stk2.push(a[i]);
                cout << "c ";
            }

            while (true)
                if (stk1.size() && stk1.top() == now)
                {
                    stk1.pop();
                    cout << "b ";
                    now++;
                }
            else if (stk2.size() && stk2.top() == now)
            {
                stk2.pop();
                cout << "d ";
                now++;
            }
            else break;
        }
        cout << endl;
    }
    return 0;
}

 

3. 最优贸易

来源:NOIP2009提高组 https://ac.nowcoder.com/acm/contest/257/C

算法知识点: SPFA

复杂度:

解题思路:

先求出:

  • 走到 的过程中,买入水晶球的最低价格 dmin[i]
  • 走到 的过程中,卖出水晶球的最高价格 dmax[i]

然后枚举每个城市作为买卖的中间城市,求出 dmax[i] - dmin[i] 的最大值即可。

dmin[i]dmax[i] 时,由于不是拓扑图,状态的更新可能存在环,因此不能使用动态规划,只能使用求最短路的方式。

C++ 代码:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std; const int N = 100010,
    M = 2000010;

int n, m;
int price[N];
int h[N], rh[N], e[M], ne[M], idx;
int dmax[N], dmin[M];
bool st[N];

void add(int *h, int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void spfa(int *d, int sign)
{
    queue<int> q;
    memset(st, false, sizeof st);
    if (sign == 1) memset(d, 0x3f, sizeof dmax);

    if (sign == 1) q.push(1), st[1] = true;
    else q.push(n), st[n] = true;

    while (q.size())
    {
        int t = q.front();
        q.pop();

        for (int i = sign == 1 ? h[t] : rh[t]; ~i; i = ne[i])
        {
            int j = e[i];

            if (sign == 1 && d[j] > min(d[t], price[j]) || sign == -1 && d[j] < max(d[t], price[j]))
            {
                if (sign == 1) d[j] = min(d[t], price[j]);
                else d[j] = max(d[t], price[j]);

                if (!st[j])
                {
                    st[j] = true;
                    q.push(j);
                }
            }
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);
    memset(rh, -1, sizeof rh);

    for (int i = 1; i <= n; i++) scanf("%d", &price[i]);

    while (m--)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(h, a, b), add(rh, b, a);
        if (c == 2) add(h, b, a), add(rh, a, b);
    }

    spfa(dmin, 1);
    spfa(dmax, -1);

    int res = 0;
    for (int i = 1; i <= n; i++) res = max(res, dmax[i] - dmin[i]);

    printf("%d\n", res);

    return 0;
}

 

4. 信息传递

来源:NOIP2015提高组 https://ac.nowcoder.com/acm/contest/263/E

算法知识点: 图论,找环

复杂度:

解题思路:

由题意,我们需要在所有点的出度均是1的有向图中,求出最小环的长度。

首先我们考虑一下所有点的出度均是1的有向图的性质:即一个环上挂着很多路径,而且不管从哪个点出发,最终都会走到某个环上。

因此我们可以借助于栈结构来找出所有环:

  • 从前往后扫描每个点,如果当前点没有被遍历过,则沿着出边一直走,直到走到已经被遍历过的点为止,走的过程中将所有点按顺序存入栈中。此时会有两种情况:

    • 此时走到的已被遍历过的点在栈中,则栈中从该点开始,到当前点这部分就是环上的所有点;
    • 此时走到的已被遍历过的点不在栈中,则说明当前只是在某个分支上走,并没有走到某个环上;

所有环的长度的最小值即是答案。

C++ 代码:

#include <iostream>
#include <algorithm>
using namespace std; const int N = 200010;

int n;
int p[N], stk[N];
bool st[N], in_stk[N];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &p[i]);

    int res = n;
    for (int i = 1; i <= n; i++)
        if (!st[i])
        {
            int tt = 0;
            int j = i;
            while (!st[j])
            {
                stk[++tt] = j;
                st[j] = true;
                in_stk[j] = true;
                j = p[j];
            }
            if (in_stk[j])
            {
                int cnt = 1;
                while (stk[tt] != j)
                {
                    in_stk[stk[tt--]] = false;
                    cnt++;
                }
                res = min(res, cnt);
            }
            while (tt) in_stk[stk[tt--]] = false;
        }

    printf("%d\n", res);
    return 0;
}

 
另外,牛客暑期NOIP真题班限时免费报名
报名链接:https://www.nowcoder.com/courses/cover/live/248
报名优惠券:DCYxdCJ