第十一次CCF计算机软件能力认证

第一题:打酱油

小明带着 N 元钱去买酱油。

酱油 10 块钱一瓶,商家进行促销,每买 3 瓶送 1 瓶,或者每买 5 瓶送 2 瓶。

请问小明最多可以得到多少瓶酱油。

输入格式

输入的第一行包含一个整数 N,表示小明可用于买酱油的钱数。

输出格式

输出一个整数,表示小明最多可以得到多少瓶酱油。

数据范围

N 是 10 的整数倍,N 不超过 300。

输入样例1:

40

输出样例1:

5

样例1解释

把 40 元分成 30 元和 10 元,分别买 3 瓶和 1 瓶,其中 3 瓶送 1 瓶,共得到 5 瓶。

输入样例2:

80

输出样例2:

11

样例2解释

把 80 元分成 30 元和 50 元,分别买 3 瓶和 5 瓶,其中 3 瓶送 1 瓶,5 瓶送 2 瓶,共得到 11 瓶。

解题思路:

暴力搜索,使用深度优先遍历,枚举每一种情况,计算出所使用的瓶子个数。

#include

using namespace std;

int res = 0;
void dfs(int u , int cnt)
{
    if(u < 0) return ;
    else if(u == 0) 
    {
        res = max(res , cnt);
        return ;
    }
    
    dfs(u - 10 , cnt + 1);
    dfs(u - 30 , cnt + 4);
    dfs(u - 50 , cnt + 7);
}

int main()
{
    int n;
    cin >> n;
    dfs(n , 0);
    cout << res << endl;
    return 0;
}

第二题:公共钥匙盒 

有一个学校的老师共用 N 个教室,按照规定,所有的钥匙都必须放在公共钥匙盒里,老师不能带钥匙回家。

每次老师上课前,都从公共钥匙盒里找到自己上课的教室的钥匙去开门,上完课后,再将钥匙放回到钥匙盒中。

钥匙盒一共有 N 个挂钩,从左到右排成一排,用来挂 N 个教室的钥匙。

一串钥匙没有固定的悬挂位置,但钥匙上有标识,所以老师们不会弄混钥匙。

每次取钥匙的时候,老师们都会找到自己所需要的钥匙将其取走,而不会移动其他钥匙。

每次还钥匙的时候,还钥匙的老师会找到最左边的空的挂钩,将钥匙挂在这个挂钩上。

如果有多位老师还钥匙,则他们按钥匙编号从小到大的顺序还。

如果同一时刻既有老师还钥匙又有老师取钥匙,则老师们会先将钥匙全还回去再取出。

今天开始的时候钥匙是按编号从小到大的顺序放在钥匙盒里的。

有 K 位老师要上课,给出每位老师所需要的钥匙、开始上课的时间和上课的时长,假设下课时间就是还钥匙时间,请问最终钥匙盒里面钥匙的顺序是怎样的?

输入格式

输入的第一行包含两个整数 N,K。

接下来 K 行,每行三个整数 w,s,c,分别表示一位老师要使用的钥匙编号、开始上课的时间和上课的时长。可能有多位老师使用同一把钥匙,但是老师使用钥匙的时间不会重叠。

保证输入数据满足输入格式,你不用检查数据合法性。

输出格式

输出一行,包含 N 个整数,相邻整数间用一个空格分隔,依次表示每个挂钩上挂的钥匙编号。

数据范围

对于 30% 的评测用例,1≤N,K≤10,1≤w≤N,1≤s,c≤30;
对于 60% 的评测用例,1≤N,K≤50,1≤w≤N,1≤s≤300,1≤c≤50,
对于所有评测用例,1≤N,K≤1000,1≤w≤N,1≤s≤10000,1≤c≤100。

输入样例1:

5 2
4 3 3
2 2 7

输出样例1:

1 4 3 2 5

样例1解释

第一位老师从时刻 3 开始使用 4 号教室的钥匙,使用 3 单位时间,所以在时刻 6 还钥匙。

第二位老师从时刻 2 开始使用钥匙,使用 7 单位时间,所以在时刻 9 还钥匙。

每个关键时刻后的钥匙状态如下(X表示空):

时刻 2 后为 1X345;
时刻 3 后为 1X3X5;
时刻 6 后为 143X5;
时刻 9 后为 14325。

输入样例2:

5 7
1 1 14
3 3 12
1 15 12
2 7 20
3 18 12
4 21 19
5 30 9

输出样例2:

1 2 3 5 4

 解题思路:

使用结构体进行存储时间信息。

如果需要取钥匙,那么就枚举每一个钥匙编号,判断该位置是否需要取出,如果取出就将其置为0

如果需要放钥匙,那么就枚举出编号为0的第一个位置,将其置为当前id

注意首先需要对于时间进行排序。

#include
#include

using namespace std;

const int N = 10010;

struct time_p
{
    int id , s , type;
}ti[N];

int n , k;
int res[N];

bool cmp(time_p a , time_p b)
{
    if(a.s != b.s) return a.s < b.s;
    
    if(a.type != b.type) return a.type > b.type;
    return a.id < b.id;
}

int main()
{
    cin >> n >> k;
    int cnt = 0;
    while(k --)
    {
        int a , b , c;
        cin >> a >> b >> c;
        ti[cnt ++] = {a , b , 0}; // 取钥匙
        ti[cnt ++] = {a , b + c , 1}; // 放钥匙
    }
    
    sort(ti , ti + cnt , cmp);
    
    for (int i = 1; i <= n; i ++ ) res[i] = i;

    for (int i = 0; i < cnt; i ++ )
    {
        int id = ti[i].id;
        if (!ti[i].type) // 取钥匙
        {
            for (int j = 1; j <= n; j ++ )
                if (res[j] == id)
                {
                    res[j] = 0;
                    break;
                }
        }
        else
        {
            for (int j = 1; j <= n; j ++ )
                if (!res[j])
                {
                    res[j] = id;
                    break;
                }
        }
    }
    
    for (int i = 1; i <= n; i ++ )
        cout << res[i] << ' ';
    
    return 0;
}

第三题:JSON查询

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,可以用来描述半结构化的数据。

JSON 格式中的基本单元是值 (value),出于简化的目的本题只涉及 2 种类型的值:

  • 字符串 (string):字符串是由双引号 " 括起来的一组字符(可以为空)。如果字符串的内容中出现双引号 ",在双引号前面加反斜杠,也就是用 \" 表示;如果出现反斜杠 \,则用两个反斜杠 \\ 表示。反斜杠后面不能出现 " 和 \ 以外的字符。例如:""、"hello"、"\"\\"
  • 对象 (object):对象是一组键值对的无序集合(可以为空)。键值对表示对象的属性,键是属性名,值是属性的内容。对象以左花括号 { 开始,右花括号 } 结束,键值对之间以逗号 , 分隔。一个键值对的键和值之间以冒号 : 分隔。键必须是字符串,同一个对象所有键值对的键必须两两都不相同;值可以是字符串,也可以是另一个对象。例如:{}、{"foo": "bar"}、{"Mon": "weekday", "Tue": "weekday", "Sun": "weekend"}

除了字符串内部的位置,其他位置都可以插入一个或多个空格使得 JSON 的呈现更加美观,也可以在一些地方换行,不会影响所表示的数据内容。

例如,上面举例的最后一个 JSON 数据也可以写成如下形式。

{
"Mon": "weekday",
"Tue": "weekday",
"Sun": "weekend"
}

给出一个 JSON 格式描述的数据,以及若干查询,编程返回这些查询的结果。

输入格式

第一行是两个正整数 n 和 m,分别表示 JSON 数据的行数和查询的个数。

接下来 n 行,描述一个 JSON 数据,保证输入是一个合法的 JSON 对象。

接下来 m 行,每行描述一个查询。给出要查询的属性名,要求返回对应属性的内容。需要支持多层查询,各层的属性名之间用小数点 . 连接。保证查询的格式都是合法的。

输出格式

对于输入的每一个查询,按顺序输出查询结果,每个结果占一行。

如果查询结果是一个字符串,则输出 STRING ,其中  是字符串的值,中间用一个空格分隔。

如果查询结果是一个对象,则输出 OBJECT,不需要输出对象的内容。

如果查询结果不存在,则输出 NOTEXIST

数据范围

n≤100,每行不超过 80 个字符。
m≤100,每个查询的长度不超过 80 个字符。
字符串中的字符均为 ASCII 码 33−126 的可打印字符,不会出现空格所有字符串都不是空串
所有作为键的字符串不会包含小数点 .。查询时键的大小写敏感
50% 的评测用例输入的对象只有 1 层结构,80% 的评测用例输入的对象结构层数不超过 2 层。
举例来说,{"a": "b"} 是一层结构的对象,{"a": {"b": "c"}} 是二层结构的对象,以此类推。

输入样例:

10 5
{
"firstName": "John",
"lastName": "Smith",
"address": {
"streetAddress": "2ndStreet",
"city": "NewYork",
"state": "NY"
},
"esc\\aped": "\"hello\""
}
firstName
address
address.city
address.postal
esc\aped

输出样例:

STRING John
OBJECT
STRING NewYork
NOTEXIST
STRING "hello"

解题思路: 

可以使用python中的stdin进行读入,然后使用eval转化为字典,然后即可判断出来

from sys import stdin

n, m = map(int , input().split())

str = ""
for i in range(n):
    str += stdin.readline()

obj = eval(str)

def query(qs):
    o = obj
    for s in qs:
        # 判断是否是{}字典
        if type(o) is not dict or s not in o:
            print("NOTEXIST")
            return
        else:
            o = o[s]
    # 字典输出object
    if type(o) is dict:
        print("OBJECT")
    else:
    # 字符串输出字符串中的内容
        print("STRING %s" % (o))

for i in range(m):
    qs = input().split('.')
    query(qs)

第四题:通信网络

某国的军队由 N 个部门组成,为了提高安全性,部门之间建立了 M 条通路,每条通路只能单向传递信息,即一条从部门 a 到部门 b 的通路只能由 a 向 b 传递信息。

信息可以通过中转的方式进行传递,即如果 a 能将信息传递到 b,b 又能将信息传递到 c,则 a 能将信息传递到 c。

一条信息可能通过多次中转最终到达目的地。

由于保密工作做得很好,并不是所有部门之间都互相知道彼此的存在。

只有当两个部门之间可以直接或间接传递信息时,他们才彼此知道对方的存在。

部门之间不会把自己知道哪些部门告诉其他部门。

第十一次CCF计算机软件能力认证_第1张图片

上图中给了一个 4 个部门的例子,图中的单向边表示通路。

部门 1 可以将消息发送给所有部门,部门 4 可以接收所有部门的消息,所以部门 1 和部门 4 知道所有其他部门的存在。

部门 2 和部门 3 之间没有任何方式可以发送消息,所以部门 2 和部门 3 互相不知道彼此的存在。

现在请问,有多少个部门知道所有 N 个部门的存在。

或者说,有多少个部门所知道的部门数量(包括自己)正好是 N。

输入格式

输入的第一行包含两个整数 N,M,分别表示部门的数量和单向通路的数量。所有部门从 1 到 N 标号。

接下来 M 行,每行两个整数 a,b,表示部门 a 到部门 b 有一条单向通路。

输出格式

输出一行,包含一个整数,表示答案。

数据范围

对于 30% 的评测用例,1≤N≤10,1≤M≤20;
对于 60% 的评测用例,1≤N≤100,1≤M≤1000;
对于 100% 的评测用例,1≤N≤1000,1≤M≤10000。

输入样例:

4 4
1 2
1 3
2 4
3 4

输出样例:

2

样例解释

部门 1 和部门 4 知道所有其他部门的存在。

 

 解题思路:

正向建图和反向建图,然后做一遍dfs走一遍图。统计出正向和反向之后的都知道的部门数。

#include
#include

using namespace std;

const int N = 1010 , M = 20010;
int n , m;
bool st[M];
bool st1[M];
int h[M] , h1[M] , ne[M] , e[M] , idx;

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

void dfs(int u , bool st[] , int h[])
{
    st[u] = true;
    for(int i = h[u];~i;i = ne[i])
    {
        int j = e[i];
        if(!st[j]) dfs(j , st , h);
    }
}


int main()
{
    cin >> n >> m;
    memset(h , -1 , sizeof h);
    memset(h1 , -1 , sizeof h1);
    
    while(m --)
    {
        int a , b;
        cin >> a >> b;
        add(h , a , b) , add(h1 , b , a);
    }
    
    int res = 0;
    for(int i = 1;i <= n;i ++)
    {
        memset(st , 0 , sizeof st);
        memset(st1 , 0 , sizeof st1);
        dfs(i , st , h) , dfs(i , st1 , h1);
        
        int t = 0;
        for(int j = 1;j <= n;j ++)
            if(st[j] || st1[j]) t ++;
        
        if(t == n) res ++;
    }
    cout << res << endl;
    return 0;
}

第五题:除法

使用线段树或是树状数组就行

 

#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;
const int N = 100010, M = 1000010;

int n, m;
set pos[M];
LL tr[N];
set vs;
struct Query
{
    int t, l, r, v;
}q[N];
int w[N];

int lowbit(int x)
{
    return x & -x;
}

void add(int x, int v)
{
    for (int i = x; i <= n; i += lowbit(i)) tr[i] += v;
}

LL query(int x)
{
    LL res = 0;
    for (int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &w[i]);
        add(i, w[i]);
    }
    for (int i = 0; i < m; i ++ )
    {
        int t, l, r, v;
        scanf("%d%d%d", &t, &l, &r);
        if (t == 1)
        {
            scanf("%d", &v);
            q[i] = {t, l, r, v};
            if (v != 1) vs.insert(v);
        }
        else q[i] = {t, l, r};
    }
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j * j <= w[i]; j ++ )
            if (w[i] % j == 0)
            {
                if (vs.count(j)) pos[j].insert(i);
                if (w[i] / j != j && vs.count(w[i] / j))
                    pos[w[i] / j].insert(i);
            }

    for (int i = 0; i < m; i ++ )
        if (q[i].t == 1)
        {
            int v = q[i].v;
            auto it = pos[v].lower_bound(q[i].l);
            while (it != pos[v].end() && *it <= q[i].r)
            {
                int k = *it;
                it ++ ;
                add(k, -w[k] + w[k] / v);

                for (int j = 1; j * j <= w[k]; j ++ )
                    if (w[k] % j == 0)
                    {
                        pos[j].erase(k);
                        if (w[k] / j != j) pos[w[k] / j].erase(k);
                    }
                w[k] /= v;
                for (int j = 1; j * j <= w[k]; j ++ )
                    if (w[k] % j == 0)
                    {
                        if (vs.count(j)) pos[j].insert(k);
                        if (w[k] / j != j && vs.count(w[k] / j))
                            pos[w[k] / j].insert(k);
                    }
            }
        }
        else printf("%lld\n", query(q[i].r) - query(q[i].l - 1));
    return 0;
}

30分直接到手 

#include

using namespace std;

const int N = 1e5 + 10;
int n , m;
int a[N];

int main()
{
    scanf("%d %d" ,&n ,&m);
    for(int i = 1;i <= n;i ++)
        scanf("%d" ,&a[i]);
    
    while(m --)
    {
        int op;
        scanf("%d" ,&op);
        if(op == 1)
        {
            int l , r , v;
            scanf("%d %d %d" ,&l ,&r ,&v);
            for(int i = l;i <= r;i ++)
                if(a[i] % v == 0) a[i] /= v;
        }
        else
        {
            int l , r;
            scanf("%d %d" ,&l ,&r);
            int sum = 0;
            for(int i = l;i <= r;i ++)
                sum += a[i];
            printf("%d\n" , sum);
        }
    }
    return 0;
}

你可能感兴趣的:(ccf,csp,算法,数据结构)