第十关——CCF线上赛 普及组

14:30:35 背影是真的,人是假的,没什么执着。——王菲《百年孤寂》

第一题 文具订购

题目描述

小明的班上共有 n 元班费,同学们准备使用班费集体购买 3 种物品:

  1. 圆规,每个 77 元。

  2. 笔,每支 44 元。

  3. 笔记本,每本 33 元。

小明负责订购文具,设圆规,笔,笔记本的订购数量分别为 a,b,c,他订购的原则依次如下:

  1. n元钱必须正好用光,即 7a+4b+3c=n

  2. 在满足以上条件情况下,成套的数量尽可能大,即 a,b,c 中的最小值尽可能大。

  3. 在满足以上条件情况下,物品的总数尽可能大,即 a+b+c 尽可能大。

请你帮助小明求出满足条件的最优方案。可以证明若存在方案,则最优方案唯一。

输入格式

输入仅一行一个整数,代表班费数量 n

输出格式

如果问题无解,请输出 -1

否则输出一行三个用空格隔开的整数 a, b, c分别代表圆规、笔、笔记本的个数。

输入输出样例

输入 #1

1

输出 #1

-1

输入 #2

14

输出 #2

1 1 1

输入 #3

33

输出 #3

1 2 6

说明/提示

样例输入输出 3 解释

a=2,b=4,c=1也是满足条件 1,2的方案,但对于条件 3,该方案只买了 7个物品,不如 a=1,b=2,c=6的方案。

数据规模与约定

  • 对于测试点 16,保证 n leq 14n14。

  • 对于测试点 712,保证 n 是 14的倍数。

  • 对于测试点 1318,保证 n100。

  • 对于全部的测试点,保证 0n100000

这道题可以用暴力枚举, 由7a+4b+3c=n可知,a是abc中最小的一个数,一定小于n/14,于是这就是第一个循环,第二个循环与第三个循环的条件上限我是选的最小的一个写,然后就是一个简单的if语句输出后就直接return,防止有两组情况,因为 a,b,c 中的最小值尽可能大,所以第一个循环是从大到小--。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    scanf("%d",&n);
    if(n==0)
    {
        printf("0 0 0");
        return 0;
    }
    for(int i=n/14;i>=0;i--)
    for(int j=i;j<=n/3;j++)
    for(int k=i;k<=n/3;k++)
    {
        if(7*i+4*j+3*k==n)
        {
            printf("%d %d %d",i,j,k);
            return 0;
        }
    }
    printf("-1");
    return 0;
}

第二题 跑步

题目描述

小 H 是一个热爱运动的孩子,某天他想给自己制定一个跑步计划。小 H 计划跑 nn 米,其中第 i(i geq 1)i(i1)分钟要跑 x_ixi 米(x_ixi 是正整数),但没有确定好总时长。

由于随着跑步时间增加,小 H 会越来越累,所以小 H 的计划必须满足对于任意 i(i >1)i(i>1) 都满足 x_i leq x_{i-1}xixi1

现在小 H 想知道一共有多少个不同的满足条件的计划,请你帮助他。两个计划不同当且仅当跑步的总时长不同,或者存在一个 ii,使得两个计划中 x_ixi 不相同。

由于最后的答案可能很大,你只需要求出答案对 pp 取模的结果。

输入格式

输入只有一行两个整数,代表总米数 nn 和模数 pp。

输出格式

输出一行一个整数,代表答案对 pp 取模的结果。

输入输出样例

输入 #1

4 44

输出 #1

5

输入 #2

66 666666

输出 #2

323522

输入 #3

66666 66666666

输出 #3

45183149

说明/提示

样例输入输出 1 解释

五个不同的计划分别是:{1,1,1,1},{2,1,1},{3,1},{2,2},{4}。


数据规模与约定

本题共 1010 个测试点,各测试点信息如下表。

测试点编号

n

测试点编号

n

11

55

66

2 imes 10^32×103

22

1010

77

5 imes 10^35×103

33

5050

88

2 imes 10^42×104

44

100100

99

5 imes 10^45×104

55

500500

1010

10^5105

对于全部的测试点,保证1n10^5,1p<2^30。

70分代码就是找规律然后递推???但是我听他们说这叫DP???,不管了,放代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll f[100001];
int n,p;
int main()
{
    scanf("%d%d",&n,&p);
    f[0]=1;
    for(int i=1;i<=n;++i)
    for(int j=i;j<=n;++j)
    f[j]=f[j]%p+f[j-i]%p;
    printf("%d",f[n]);
    return 0;
}

真的是短到我无话可说!

100分代码是标准dp,是一个二维dp,思路差不多吧,注意最后要清空o的值。(经常忘然后就会检查好久好久!!!)

#include<bits/stdc++.h>
#define ll long long 
using namespace std; 
ll d[100001],f[1001][100001];
int main()
{
    int n,p;
    scanf("%d%d",&n,&p);
    int m=sqrt(n)+1;
    d[0]=f[0][0]=1;
    for(int i=1;i<m;i++)
    {
        for(int j=i;j<=n;j++)
        {
            d[j]=(d[j]+d[j-i])%p;
        }
    }
    for(int i=1;i<m;i++)
    {
        for(int j=i;j<=n;j++)
        {
            f[i][j]=f[i][j-i];
            if(j>=m) f[i][j]=(f[i][j]+f[i-1][j-m])%p;
        }
    }
    ll ans=0,o=0;
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<m;j++)
        {
            o=(o+f[j][n-i])%p;
        }
        ans+=o*d[i];
        ans%=p;
        o=0;
    }
    printf("%d",ans);
    return 0;
}

第三题  魔法

题目描述

C 国由 n 座城市与 m 条有向道路组成,城市与道路都从 1 开始编号,经过 i 号道路需要 ti 的费用。

现在你要从 1 号城市出发去 n 号城市,你可以施展最多 k 次魔法,使得通过下一条道路时,需要的费用变为原来的相反数,即费用从 ti 变为ti。请你算一算,你至少要花费多少费用才能完成这次旅程。

注意:使用魔法只是改变一次的花费,而不改变一条道路自身的 ti;最终的费用可以为负,并且一个城市可以经过多次(包括 n 号城市)。

输入格式

输入的第一行有三个整数,分别代表城市数 n,道路数 m 和魔法次数限制 k。

第 2 到第(m+1) 行,每行三个整数。第 (i+1) 行的整数 ui,vi,ti 表示存在一条从 ui 到 vi 的单向道路,经过它需要花费ti

输出格式

输出一行一个整数表示最小的花费。

输入输出样例

输入 #1

4 3 2

1 2 5

2 3 4

3 4 1

输出 #1

-8

输入 #2

2 2 2

1 2 10

2 1 1

输出 #2

-19

说明/提示

输入输出样例 1 解释

依次经过 1 号道路、2 号道路、3 号道路,并在经过 1,2 号道路前使用魔法。

输入输出样例 2 解释

依次经过 1 号道路、2号道路、1 号道路,并在两次经过 1号道路前都使用魔法。

SPFA80分代码,改了很久,思路不是特别清楚,但是还是超时(注意变量赋值一定要大!!!很大很大的那种)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
typedef pair <int,int> P;
int n,m,k,h[102],vis[102][200004];
ll d[102][200004];
queue <P> q;
struct sby{
    int to,nt,len;
} e[1000003];
void spfa() {
    for(int i=1; i<=n; i++) 
    for(int j=0; j<=k; j++) 
    d[i][j]=999999999;
    d[1][0]=0;
    q.push(P(1,0));
    vis[1][0]=1;
    while(!q.empty()) 
    {
        int a=q.front().first;
        int x=q.front().second;
        q.pop();
        vis[a][x]=0;
        for(int i=h[a];i;i=e[i].nt) 
        {
            int y=e[i].to,z=e[i].len;
            if(d[y][x]>d[a][x]+z) 
            {
                d[y][x]=d[a][x]+z;
                if(!vis[y][x]) 
                {
                    q.push(P(y,x));
                    vis[y][x]=1;
                }
            }
            if(x<k && d[y][x+1]>d[a][x]-z) 
            {
                d[y][x+1]=d[a][x]-z;
                if(!vis[y][x+1]) 
                {
                    q.push(P(y,x+1));
                    vis[y][x+1]=1;
                }
            }
        }
    }
}
int main() {
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1; i<=m; i++) {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        e[i].to=y;
        e[i].nt=h[x];
        e[i].len=z;
        h[x]=i;
    }
    spfa();
    ll ans=999999999;
    for(int i=0; i<=k; i++) 
    ans=min(ans,d[n][i]);
    printf("%lld
",ans);
    return 0;
}

正在想办法可不可以利用一些手段省一些时间复杂度,不用把整篇代码都改了

原文地址:https://www.cnblogs.com/wybxz/p/12494256.html