CCF 2017 真题 持续更新ing

分蛋糕

描述

问题描述
  小明今天生日,他有n块蛋糕要分给朋友们吃,这n块蛋糕(编号为1到n)的重量分别为a1, a2, …, an。小明想分给每个朋友至少重量为k的蛋糕。小明的朋友们已经排好队准备领蛋糕,对于每个朋友,小明总是先将自己手中编号最小的蛋糕分给他,当这个朋友所分得蛋糕的重量不到k时,再继续将剩下的蛋糕中编号最小的给他,直到小明的蛋糕分完或者这个朋友分到的蛋糕的总重量大于等于k。
  请问当小明的蛋糕分完时,总共有多少个朋友分到了蛋糕。
输入格式
  输入的第一行包含了两个整数n, k,意义如上所述。
  第二行包含n个正整数,依次表示a1, a2, …, an。
输出格式
  输出一个整数,表示有多少个朋友分到了蛋糕。
样例输入
6 9
2 6 5 6 3 5
样例输出
3
样例说明
  第一个朋友分到了前3块蛋糕,第二个朋友分到了第4、5块蛋糕,第三个朋友分到了最后一块蛋糕。
评测用例规模与约定
  对于所有评测用例,1 ≤ n ≤ 1000,1 ≤ k ≤ 10000,1 ≤ ai ≤ 1000。

分析

这个比较适合于用队列来做,按顺序添加进去,然后每次取最前面的,直到累加> k为止,依次pop出来

补充一点关于的知识:
queueq 声明
没有clear(),所以需要手动 while(!q.empty())q.pop()
这里没有top,要取的是q.front()或q.back()
q.pop()和q.push()还是有的
q.empty()返回bool是否为空
q.size()队列大小


会觉得混是因为这个头文件下同样规定了优先队列priority_queueq;这里因为有优先级,所以用的是q.top() q.pop()这样

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
queue<int>cakes;
int main()
{
    int n,k;
    while(~scanf("%d%d",&n,&k))
    {
        while(!cakes.empty())cakes.pop(); 
        int w;
        for(int i = 0;i<n;i++)
        {
            scanf("%d",&w);
            cakes.push(w);
        }
        int ans = 0;
        while(!cakes.empty() )
        {
            int t=0,sum = 0;
            while(sum<k && !cakes.empty())
            {
             t = cakes.front();
             sum += t;
             cakes.pop();
            }
            ans ++;
        }
        cout<<ans<<"\n";
    }
}

学生排队

描述

问题描述
  体育老师小明要将自己班上的学生按顺序排队。他首先让学生按学号从小到大的顺序排成一排,学号小的排在前面,然后进行多次调整。一次调整小明可能让一位同学出队,向前或者向后移动一段距离后再插入队列。
  例如,下面给出了一组移动的例子,例子中学生的人数为8人。
  0)初始队列中学生的学号依次为1, 2, 3, 4, 5, 6, 7, 8;
  1)第一次调整,命令为“3号同学向后移动2”,表示3号同学出队,向后移动2名同学的距离,再插入到队列中,新队列中学生的学号依次为1, 2, 4, 5, 3, 6, 7, 8;
  2)第二次调整,命令为“8号同学向前移动3”,表示8号同学出队,向前移动3名同学的距离,再插入到队列中,新队列中学生的学号依次为1, 2, 4, 5, 8, 3, 6, 7;
  3)第三次调整,命令为“3号同学向前移动2”,表示3号同学出队,向前移动2名同学的距离,再插入到队列中,新队列中学生的学号依次为1, 2, 4, 3, 5, 8, 6, 7。
  小明记录了所有调整的过程,请问,最终从前向后所有学生的学号依次是多少?
  请特别注意,上述移动过程中所涉及的号码指的是学号,而不是在队伍中的位置。在向后移动时,移动的距离不超过对应同学后面的人数,如果向后移动的距离正好等于对应同学后面的人数则该同学会移动到队列的最后面。在向前移动时,移动的距离不超过对应同学前面的人数,如果向前移动的距离正好等于对应同学前面的人数则该同学会移动到队列的最前面。
输入格式
  输入的第一行包含一个整数n,表示学生的数量,学生的学号由1到n编号。
  第二行包含一个整数m,表示调整的次数。
  接下来m行,每行两个整数p, q,如果q为正,表示学号为p的同学向后移动q,如果q为负,表示学号为p的同学向前移动-q。
输出格式
  输出一行,包含n个整数,相邻两个整数之间由一个空格分隔,表示最终从前向后所有学生的学号。
样例输入
8
3
3 2
8 -3
3 -2
样例输出
1 2 4 3 5 8 6 7
评测用例规模与约定
  对于所有评测用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 1000,所有移动均合法

分析

其实就是双向链表的应用
但是双向链表过于不熟悉
以至于一直错一直错qaq
声明

struct Node
{
   int id;
   Node * front; Node * next;
};

然后双向链表得有头有尾,而单向链表只需要有头就可以了
Node * head; Node * tail;
初始化

head = new Node();
head->next = NULL;
head->front = NULL;
tail = head;

建表

void creatList(int id)
{
  Node * p = new Node();
  p->id = id;
  p->next = NULL;
  p->front = tali;

  tail->next = p;
  tail = p;
}

这是向后面加点,所以是顺序的表

在某个位置插入点,关键要记得同时修改front和next,之前一直有错就是这里的同步不对
在节点n之前插入节点p:

void insert(Node *n, Node *p)
{
  p->front = n->front;
  p->next = n;
  if(n->front)n->front->next = p;
  n->front=p;
}

在节点n之后插入p:

void insert(Node *n, Node*p)
{
  p->next = n->next;
  p->front = n;
  if(p->next)n->next->front  = p;
  n->next = p;
}

单链表的话两个关系就行了,双向链表就需要4个

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
struct Node {
    int id;
    Node * next;
    Node * front;
};
Node * head;
Node * tail;
void output()
{
    Node * p = head->next;
    while (p != NULL)
    {
        cout << p->id << " ";
        p = p->next;
    }
    cout << "\n";
}
void createList(int i)
{
    Node * p = new Node();
    p->id = i;
    p->next = NULL;
    p->front = tail;
    tail->next = p;
    tail = p;
}
void toFront(int id, int l)
{

    Node * p = head->next;
    Node * pfront = head;
    while (p != NULL)
    {
        if (p->id == id)break;
        pfront = p;
        p = p->next;
    }
    pfront->next = p->next;
    if(p->next)p->next->front = pfront;
    //output();
    Node * n = p;
    while (l)
    {
        n = n->front;
        l--;
    }

    p->front = n->front;
    p->next = n;
    n->front->next = p;
    n->front = p;


}
void toBack(int id, int l)
{
    Node * p = head->next;
    Node * pfront = head;
    while (p != NULL)
    {
        if (p->id == id)break;
        pfront = p;
        p = p->next;
    }
    pfront->next = p->next;
    p->next->front = p->front;
    Node * n = p;
    while (l)
    {
        n = n->next;
        l--;
    }
    p->next = n->next;
    p->front = n;
    if(n->next)n->next->front = p;
    n->next = p;

}

int main()
{
    int n, m, p, q;
    while (~scanf("%d", &n))
    {
        head = new Node();
        head->next = NULL;
        head->front = NULL;
        tail = new Node();
        tail = head;
        for (int i = 1; i <= n; i++)
        {
            createList(i);
        }

        scanf("%d", &m);
        for (int i = 0; i<m; i++)
        {
            scanf("%d%d", &p, &q);
            if (q>0)toBack(p, q);
            else toFront(p, -q);
            //output();
        }
        output();
    }
}

Markdown

描述

问题描述
  Markdown 是一种很流行的轻量级标记语言(lightweight markup language),广泛用于撰写带格式的文档。例如以下这段文本就是用 Markdown 的语法写成的:

  这些用 Markdown 写成的文本,尽管本身是纯文本格式,然而读者可以很容易地看出它的文档结构。同时,还有很多工具可以自动把 Markdown 文本转换成 HTML 甚至 Word、PDF 等格式,取得更好的排版效果。例如上面这段文本通过转化得到的 HTML 代码如下所示:

  本题要求由你来编写一个 Markdown 的转换工具,完成 Markdown 文本到 HTML 代码的转换工作。简化起见,本题定义的 Markdown 语法规则和转换规则描述如下:
  ●区块:区块是文档的顶级结构。本题的 Markdown 语法有 3 种区块格式。在输入中,相邻两个区块之间用一个或多个空行分隔。输出时删除所有分隔区块的空行。
  ○段落:一般情况下,连续多行输入构成一个段落。段落的转换规则是在段落的第一行行首插入 <p>,在最后一行行末插入 </p>
  ○标题:每个标题区块只有一行,由若干个 # 开头,接着一个或多个空格,然后是标题内容,直到行末。# 的个数决定了标题的等级。转换时,# Heading 转换为 <h1>Heading</h1>## Heading 转换为 <h2>Heading</h2>,以此类推。标题等级最深为 6。
  ○无序列表:无序列表由若干行组成,每行由 * 开头,接着一个或多个空格,然后是列表项目的文字,直到行末。转换时,在最开始插入一行 <ul>,最后插入一行 </ul>;对于每行,* Item 转换为 <li>Item</li>。本题中的无序列表只有一层,不会出现缩进的情况。
  ●行内:对于区块中的内容,有以下两种行内结构。
  ○强调:_Text_ 转换为 <em>Text</em>。强调不会出现嵌套,每行中 _ 的个数一定是偶数,且不会连续相邻。注意 _Text_ 的前后不一定是空格字符。
  ○超级链接:[Text](Link) 转换为 <a href="Link">Text</a>。超级链接和强调可以相互嵌套,但每种格式不会超过一层。
输入格式
  输入由若干行组成,表示一个用本题规定的 Markdown 语法撰写的文档。
输出格式
  输出由若干行组成,表示输入的 Markdown 文档转换成产生的 HTML 代码。
样例输入

# Hello

Hello, world!

样例输出

Hello

Hello, world!

评测用例规模与约定
  本题的测试点满足以下条件:
  ●本题每个测试点的输入数据所包含的行数都不超过100,每行字符的个数(包括行末换行符)都不超过100。
  ●除了换行符之外,所有字符都是 ASCII 码 32 至 126 的可打印字符。
  ●每行行首和行末都不会出现空格字符。
  ●输入数据除了 Markdown 语法所需,内容中不会出现 #*_[]()<>& 这些字符。
  ●所有测试点均符合题目所规定的 Markdown 语法,你的程序不需要考虑语法错误的情况。
  每个测试点包含的语法规则如下表所示,其中“√”表示包含,“×”表示不包含。

提示
  由于本题要将输入数据当做一个文本文件来处理,要逐行读取直到文件结束,C/C++、Java 语言的用户可以参考以下代码片段来读取输入内容。

分析

规则倒是给的很清晰,,但是也的确花了很多时间才过
1 是输入的时候,getline遇到空行可能会停止。要怎么去保存一个片段,这里选择用string数组,每逢输入到空行时,其实Line==”“,这时就对之前记录下来的一个片段做处理,然后清空继续
2 输出的时候,每种情况的行内情况是一样的,需要判断强调和超链接;而强调和超链接还可能嵌套,所以用递归的调用输出比较合适
3 回车 空格等细节问题,需要非常仔细

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
string strs[105];
void handleInline(string str)
{
    int len = str.length();
    int i = 0;
    while(i<len)
    {
        if(str[i]=='_')
        {
            cout<<"<em>";
            i++;
            string sub = "";
            while(i<len && str[i]!='_')
            {
                sub += str[i];
                i++;
            }
            handleInline(sub);
            cout<<"</em>";

        }
        else if(str[i]=='[')
        {
            string text="";
            i++;
            cout<<"<a href=\"";
            while(i<len && str[i]!=']')
            {
                text += str[i];
                i++;
            }
            i++;
            i++;
            string link="";
            while(i<len && str[i]!=')')
            {
                link += str[i];i++;
            }
            handleInline(link);
            cout<<"\">";
            handleInline(text);
            cout<<"</a>";
        }
        else  cout<<str[i];
        i++;
    }
}
void handleH()
{
    int h = 0;
    int len = strs[0].length();
    int i =0;
    while(i<len && strs[0][i]=='#'){i++;
    h++;
    }
    cout<<"<h"<<h<<">";
    while(i<len && strs[0][i]==' ')i++;
    string last = strs[0].substr(i);
    handleInline(last);
    cout<<"</h"<<h<<">"<<"\n";
}
void handleLi(int cnt)
{
    cout<<"<ul>"<<"\n";
    for(int i =0;i<cnt;i++)
    {
        cout<<"<li>";
        string sub="";
        int j = 1,len = strs[i].length();
        while(j<len && strs[i][j]==' ')j++;
        sub = strs[i].substr(j);
        handleInline(sub);
        cout<<"</li>"<<"\n";
    }
    cout<<"</ul>\n";
}
void handleP(int cnt)
{
    cout<<"<p>";
    for(int i =0;i<cnt;i++)
    {
        handleInline(strs[i]);
        if(i==cnt-1)cout<<"</p>\n";
        else cout<<"\n";
    }
}
int main()
{

    int cnt = 0;
    string line;
    while(getline(cin,line))
    {   
        if(line == "")
        {
            if(cnt ==0)continue;

            if(strs[0][0]=='#')handleH();
            else if(strs[0][0]=='*')handleLi(cnt);
            else handleP(cnt);

            cnt = 0;
            continue;
        }
        strs[cnt++]=line;

    }
    if(cnt)
    {
            if(strs[0][0]=='#')handleH();
            else if(strs[0][0]=='*')handleLi(cnt);
            else handleP(cnt);

            cnt = 0;
    }

}

地铁修建

描述

问题描述
  A市有n个交通枢纽,其中1号和n号非常重要,为了加强运输能力,A市决定在1号到n号枢纽间修建一条地铁。
  地铁由很多段隧道组成,每段隧道连接两个交通枢纽。经过勘探,有m段隧道作为候选,两个交通枢纽之间最多只有一条候选的隧道,没有隧道两端连接着同一个交通枢纽。
  现在有n家隧道施工的公司,每段候选的隧道只能由一个公司施工,每家公司施工需要的天数一致。而每家公司最多只能修建一条候选隧道。所有公司同时开始施工。
  作为项目负责人,你获得了候选隧道的信息,现在你可以按自己的想法选择一部分隧道进行施工,请问修建整条地铁最少需要多少天。
输入格式
  输入的第一行包含两个整数n, m,用一个空格分隔,分别表示交通枢纽的数量和候选隧道的数量。
  第2行到第m+1行,每行包含三个整数a, b, c,表示枢纽a和枢纽b之间可以修建一条隧道,需要的时间为c天。
输出格式
  输出一个整数,修建整条地铁线路最少需要的天数。
样例输入
6 6
1 2 4
2 3 4
3 6 7
1 4 2
4 5 5
5 6 6
样例输出
6
样例说明
  可以修建的线路有两种。
  第一种经过的枢纽依次为1, 2, 3, 6,所需要的时间分别是4, 4, 7,则整条地铁线需要7天修完;
  第二种经过的枢纽依次为1, 4, 5, 6,所需要的时间分别是2, 5, 6,则整条地铁线需要6天修完。
  第二种方案所用的天数更少。
评测用例规模与约定
  对于20%的评测用例,1 ≤ n ≤ 10,1 ≤ m ≤ 20;
  对于40%的评测用例,1 ≤ n ≤ 100,1 ≤ m ≤ 1000;
  对于60%的评测用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 10000,1 ≤ c ≤ 1000;
  对于80%的评测用例,1 ≤ n ≤ 10000,1 ≤ m ≤ 100000;
  对于100%的评测用例,1 ≤ n ≤ 100000,1 ≤ m ≤ 200000,1 ≤ a, b ≤ n,1 ≤ c ≤ 1000000。

  所有评测用例保证在所有候选隧道都修通时1号枢纽可以通过隧道到达其他所有枢纽。

分析

变化后的diskstra 单源最短路径
一个问题是,之前用二维数组存储图,但是这里的数据过大,不能存储,所以用了邻接表的格式
为了不超时,能更快的找到距离现在点最少天数的点,用优先队列
优先队列里存储的pair格式,要用到typedef声明
pair的大小比较原则是,先比较第一个数,两数相同的情况下比较第二个数,所以让pair<距离,点>
声明结构体,记录边

然后总的思路就是dijkstra了
dis[]数组,表明到点i的距离(天数)
每次找到最近的点,加入已选集合(优先队列中);
然后根据新加入的点,更新到其他点的距离(天数)
只不过这里不是求和最小,决定到i的路径的长度的,是这条线上的最大天数,每次要比较新加入的点修建天数是否比之前的点的天数大了,然后进行更新

直到所有点都选完为止(优先队列为空)

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>

using namespace std;
const int maxx = 100002;
struct edge{int t,distance;
 edge(int t,int di):t(t),distance(di){};

};
int dis[maxx];
typedef pair<int,int>P;
vector<edge>G[maxx];
int n,m;
const int inf = 1<<30;
priority_queue<P, vector<P>, greater<P> >que;
void dijkstra()
{
    for(int i = 0;i<maxx;i++)dis[i]=inf;
    dis[1]=0;
    que.push(P(0,1));

    while(!que.empty())
    {
        P now = que.top();
        que.pop();
        int u = now.second;
        int di = now.first;

        //if(dis[u]< di)continue;

        for(int i = 0;i<G[u].size();i++)
        {
            edge e = G[u][i];
            if(dis[e.t]>max(dis[u],e.distance))
            {
                dis[e.t]=max(dis[u],e.distance);
                que.push(P(dis[e.t],e.t));
            }
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i = 0;i<m;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            G[a].push_back(edge(b,c));
            G[b].push_back(edge(a,c));

        }
        dijkstra();
        cout<<dis[n]<<"\n";
    }
}

猜你喜欢

转载自blog.csdn.net/BeforeEasy/article/details/82504087
今日推荐