noi online 普及组

1.

我们一个一个条件地分析:

1 要花光所有的钱:
首先,三种文具的价格是 3,4,73,4,7 ,通过证明,发现除了 n=1 ,n=2 , n=5 以外,任何的价格 n 都可以被花光,所以这个条件并没有什么太大的约束力;

2 尽量配成更多套

一套的价钱是 1414 元,所以这个规定就是要我们尽可能地买更多的整 1414 元;

3 尽量买更多的物品 :

这个规定其实就是在我们买了尽可能多的整 14 元之后,剩余的钱再尽量拆分成 3 元或 44 元(因为 7=3+4 ,这样买的物品数量就会减少,所以不买 7 元的东西)

#include<cstdio>
#include<iostream>
using namespace std;
int main()
{

    int n;
    int a,b,c;
    scanf("%d",&n);
    a=n/14;
    b=n/14;
    c=n/14;
    if (n==1||n==2||n==5)
    {
        printf("-1");
        return 0;
    }
    else if (n%14==1)
    {
        c--;
        b=b+2;
    }
    else if (n%14==2)
    {
        a=a+3;
        c--;
    }
    else if (n%14==3)
    {
        a++;
    }
    else if (n%14==4)
    {
        b++;
    }
    else if (n%14==5)
    {
        c--;
        a=a+4;
    }
    else if (n%14==6)
    {
        a=a+2;
    }
    else if (n%14==7)
    {
        a++;
        b++;
    }
    else if (n%14==8)
    {
        b=b+2;
    }
    else if (n%14==9)
    {
        a=a+3;
    }
    else if (n%14==10)
    {
        a=a+2;
        b=b+1;
    }
    else if (n%14==11)
    {
        a=a+1;
        b=b+2;
    }
    else if (n%14==12)
    {
        a=a+4;
    }
    else if (n%14==13)
    {
        a=a+3;
        b=b+1;
    }
    printf("%d %d %d",c,b,a);
    return 0;
}

2.

把一个整数N拆分为K个数之和的问题

我们可以得知,递推公式为

f(n, k) = f(n-1, k-1) + f(n-k, k)

节约内存。想了想可以尝试使用滚动数组。要使用滚动数组,我需要调整动态规划的计算方向,之前是按行更新,需要调整成按列更新

#include <bits/stdc++.h>
#define  LL long long
using namespace std;
const int N = 100005;
int f[N];
int g[400][N];
int main() {
    int n, p;
    cin >> n >> p;
    int m = sqrt(n) + 1;
    f[0] = 1;
    for (int i = 1; i < m; i++) {
        for (int j = i; j <= n; j++) {
            f[j] += f[j - i];
            f[j] %= p;
        }
    }
    g[0][0] = 1;
    for (int i = 1; i < m; i++) {
        for (int j = i; j <= n; j++) {
            g[i][j] = g[i][j - i];
            if (j >= m) g[i][j] += g[i - 1][j - m];
            g[i][j] %= p;
        }
    }
    int ans = 0;
    for (int i = 0; i <= n; i++) {
        LL sum = 0;
        for (int j = 0; j < m; j++) sum += g[j][n - i];
        sum %= p;
        ans = (ans + f[i] * sum) % p;
    }
    cout << ans << endl;
    return 0;
}

3.

k=0
直接跑Floyd 即可得出结果(传送门:Floyd算法)

k=1
设 f k,i,j 表示在使用不超过 k次魔法的情况下,从 i 到 j 的最短路。

现在我们知道了 f 0,i,j,如何得到 f 1,i,j呢?

边的规模只有 2500,我们可以直接枚举要用魔法的边,转移时强制走这条边,求最短路。

假如边 (u,v,w)用了魔,

k=2
我们已经得到了k=1 的情况,如何推 k=2 的情况呢?

类似于 Floyd,我们可以枚举一个中转点 k,i→k 最多用一次魔法,k→j 最多用一次魔法,合并起来就是最多两次魔法的答案了。

这个可以用矩阵快速幂来优化转移,最多用 i 次魔法的结果和最多用 j次魔法的结果按照上面的转移方式合并的话,可以得到最多用 i+j 次魔法的结果

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const LL INF = 0x3f3f3f3f3f3f3f3f;
const int N = 100;
typedef LL Matrix[N][N];
int sz;
LL a[N][N];
void matrix_mul(Matrix A, Matrix B, Matrix res) {
    Matrix C;
    for (int i = 0; i < sz; i++) {
        for (int j = 0; j < sz; j++) {
            C[i][j] = INF;
            for (int k = 0; k < sz; k++) {
                C[i][j] = min(C[i][j], A[i][k] + B[k][j]);

            }
        }
    }
    for (int i = 0; i < sz; i++) {
        for (int j = 0; j < sz; j++) {
            res[i][j] = C[i][j];

        }
    }
}
void matrix_pow(Matrix A, LL p, Matrix res) {
    Matrix x;
    for (int i = 0; i < sz; i++) {
        for (int j = 0; j < sz; j++) {
            x[i][j] = A[i][j];

            res[i][j] = a[i][j];

        }
    }
    while (p) {
        if (p & 1) matrix_mul(res, x, res);
        p >>= 1;
        matrix_mul(x, x, x);
    }
}

struct Edge {
    int u, v, w;
} b[2505];

int main() {
    int n, m, K;
    scanf("%d%d%d", &n, &m, &K);
    sz = n;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            a[i][j] = i == j ? 0 : INF;
    for (int i = 0; i < m; i++) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        u--;
        v--;
        b[i] = {u, v, w};
        a[u][v] = min(a[u][v], (LL)w);

    }
    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                a[i][j] = min(a[i][j], a[i][k] + a[k][j]);

            }
        }
    }
    Matrix c;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            c[i][j] = a[i][j];

            for (int k = 0; k < m; k++) {
                c[i][j] = min(c[i][j], a[i][b[k].u] - b[k].w + a[b[k].v][j]);

            }
        }
    }
    matrix_pow(c, K, c);
    printf("%lld
", c[0][n - 1]);
    return 0;
}


原文地址:https://www.cnblogs.com/Heartbeat358/p/12507268.html