CCF 2017 真题 持续更新ing

本文精选了五道算法竞赛题目,涵盖了图论、数据结构、字符串处理等多个方面,通过详细解析题目的背景、分析思路及代码实现,帮助读者深入理解算法原理与应用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

公共钥匙盒

描述

问题描述
  有一个学校的老师共用N个教室,按照规定,所有的钥匙都必须放在公共钥匙盒里,老师不能带钥匙回家。每次老师上课前,都从公共钥匙盒里找到自己上课的教室的钥匙去开门,上完课后,再将钥匙放回到钥匙盒中。
  钥匙盒一共有N个挂钩,从左到右排成一排,用来挂N个教室的钥匙。一串钥匙没有固定的悬挂位置,但钥匙上有标识,所以老师们不会弄混钥匙。
  每次取钥匙的时候,老师们都会找到自己所需要的钥匙将其取走,而不会移动其他钥匙。每次还钥匙的时候,还钥匙的老师会找到最左边的空的挂钩,将钥匙挂在这个挂钩上。如果有多位老师还钥匙,则他们按钥匙编号从小到大的顺序还。如果同一时刻既有老师还钥匙又有老师取钥匙,则老师们会先将钥匙全还回去再取出。
  今天开始的时候钥匙是按编号从小到大的顺序放在钥匙盒里的。有K位老师要上课,给出每位老师所需要的钥匙、开始上课的时间和上课的时长,假设下课时间就是还钥匙时间,请问最终钥匙盒里面钥匙的顺序是怎样的?
输入格式
  输入的第一行包含两个整数N, K。
  接下来K行,每行三个整数w, s, c,分别表示一位老师要使用的钥匙编号、开始上课的时间和上课的时长。可能有多位老师使用同一把钥匙,但是老师使用钥匙的时间不会重叠。
  保证输入数据满足输入格式,你不用检查数据合法性。
输出格式
  输出一行,包含N个整数,相邻整数间用一个空格分隔,依次表示每个挂钩上挂的钥匙编号。
样例输入
5 2
4 3 3
2 2 7
样例输出
1 4 3 2 5
样例说明
  第一位老师从时刻3开始使用4号教室的钥匙,使用3单位时间,所以在时刻6还钥匙。第二位老师从时刻2开始使用钥匙,使用7单位时间,所以在时刻9还钥匙。
  每个关键时刻后的钥匙状态如下(X表示空):
  时刻2后为1X345;
  时刻3后为1X3X5;
  时刻6后为143X5;
  时刻9后为14325。
样例输入
5 7
1 1 14
3 3 12
1 15 12
2 7 20
3 18 12
4 21 19
5 30 9
样例输出
1 2 3 5 4
评测用例规模与约定
  对于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。

分析

乍一看好像有点乱,但是从他对样例的解释中可以看到,关键的就是每个人取钥匙、还钥匙的时间节点,将所有的这些时间排序
当时间一样时,让还钥匙的早于取钥匙的,因此要在结构体中设置一个标识位,true的时候表示取钥匙,FALSE的时候表示还钥匙
然后当同时还钥匙的时候,让编号小的在前面
这样循环处理,针对每一个时间节点是取还是还做处理就好了

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
const int maxx=2002;
struct Teach{
    int w;//keyid
    int time;
    bool fetech;

};
Teach teachers[maxx];
bool cmp(const Teach& a, const Teach& b)
{
    if(a.time == b.time && a.fetech == b.fetech && a.fetech == false)
    {
        return a.w < b.w;
    }
    else if(a.time == b.time)
    {
        return a.fetech <b.fetech;
    }
    else if(a.time != b.time )return a.time<b.time;
}
int keys[maxx];
int main()
{
    int N,K;
    while(~scanf("%d%d",&N,&K))
    {
        int w,s,c,cnt = 0;
        for(int i =1;i<=N;i++)
            keys[i]=i;
        while(K--)
        {
            scanf("%d%d%d",&w,&s,&c);
            teachers[cnt].w = w;teachers[cnt].time = s;teachers[cnt].fetech = true;
            cnt++;
            teachers[cnt].w = w; teachers[cnt].time = s+c;teachers[cnt].fetech = false;
            cnt++;
        }
        sort(teachers,teachers+cnt,cmp);
        for(int i = 0;i<cnt;i++)
        {
            if(teachers[i].fetech == true)
            {
                for(int j = 1;j<=N;j++)
                {
                    if(keys[j]==teachers[i].w)
                    {
                        keys[j]=-1;
                        break;
                    }
                }
            }
            else{
                for(int j = 1;j<=N;j++)
                {
                    if(keys[j]==-1)
                    {
                        keys[j]=teachers[i].w;
                        break;
                    }
                }
            }
        }
        for(int i = 1;i<=N;i++)
        {
            if(i==N)printf("%d\n",keys[i]);
            else printf("%d ",keys[i]);
        }
//      for(int i =0;i<cnt;i++)
//      cout<<teachers[i].w<<" "<<teachers[i].time<<" "<<teachers[i].fetech<<"\n";
    }
}

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

分析

可以递归的调用,分别是解析字符串和解析对象。关键是要明确入口、出口,索引向前的顺序,何时break,否则可能会一直出问题
getline读入数据,注意前后可能遗漏换行
将每一行拼接起来当做一个字符串处理
传地址的索引位置i
以map

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
string Json[105];
int n,m;
map<string,string>dict;

string pareseString(string &str,int &i)
{
    string ans="";
    if(str[i]=='"')i++;
    int len = str.length();
    while(i<len)
    {
        if(str[i]=='\\'){
            i++;
            ans += str[i];
            i++;
        }
        else if(str[i]=='"')break;
        else
        {
            ans += str[i];
            i++;
        }       
    }
    if(str[i]=='"')i++;
    return ans;
}
void parseObject(string &str, string prefix,int &i)
{
    string key="",value="";
    if(str[i]=='{')i++;
    int len = str.length();
    bool isKey = true;
    while(i<len)
    {
        if(str[i]=='"')
        {
            string newstr = pareseString(str,i);
            if(isKey == false)
            {
                value = newstr;
                dict[key]=value;
            }
            else{
                if(prefix!="")key = prefix+'.'+newstr;
                else key = newstr;
            }

        }
        else if(str[i]==':')
        {
            isKey = false;
            i++;
        }
        else if(str[i]==',')
        {
            isKey = true;
            i++;
        }
        else if(str[i]=='{')
        {
            dict[key]="";
            parseObject(str,key,i);
        }
        else if(str[i]=='}')
        {
            break;
        }
        else i++;
    }
    if(str[i]=='}')i++;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        string jsonAll;
        getchar();
        for(int i = 0;i<n;i++)
         {
            getline(cin,Json[i]);
         }

        for(int i = 0;i<n;i++)
        {
            jsonAll += Json[i];
        }
        dict.clear();
        int i = 0;
        parseObject(jsonAll,"",i);
        map<string,string>::iterator it ;
        string query;
        while(m--)
        {
            cin>>query;
            if(dict.find(query)==dict.end())cout<<"NOTEXIST\n";
            else{
                if(dict[query]=="")cout<<"OBJECT\n";
                else cout<<"STRING "<<dict[query]<<"\n";
            }
        }
    }
}

通信网络

描述

问题描述
  某国的军队由N个部门组成,为了提高安全性,部门之间建立了M条通路,每条通路只能单向传递信息,即一条从部门a到部门b的通路只能由a向b传递信息。信息可以通过中转的方式进行传递,即如果a能将信息传递到b,b又能将信息传递到c,则a能将信息传递到c。一条信息可能通过多次中转最终到达目的地。
  由于保密工作做得很好,并不是所有部门之间都互相知道彼此的存在。只有当两个部门之间可以直接或间接传递信息时,他们才彼此知道对方的存在。部门之间不会把自己知道哪些部门告诉其他部门。

  上图中给了一个4个部门的例子,图中的单向边表示通路。部门1可以将消息发送给所有部门,部门4可以接收所有部门的消息,所以部门1和部门4知道所有其他部门的存在。部门2和部门3之间没有任何方式可以发送消息,所以部门2和部门3互相不知道彼此的存在。
  现在请问,有多少个部门知道所有N个部门的存在。或者说,有多少个部门所知道的部门数量(包括自己)正好是N。
输入格式
  输入的第一行包含两个整数N, M,分别表示部门的数量和单向通路的数量。所有部门从1到N标号。
  接下来M行,每行两个整数a, b,表示部门a到部门b有一条单向通路。
输出格式
  输出一行,包含一个整数,表示答案。
样例输入
4 4
1 2
1 3
2 4
3 4
样例输出
2
样例说明
  部门1和部门4知道所有其他部门的存在。
评测用例规模与约定
  对于30%的评测用例,1 ≤ N ≤ 10,1 ≤ M ≤ 20;
  对于60%的评测用例,1 ≤ N ≤ 100,1 ≤ M ≤ 1000;
  对于100%的评测用例,1 ≤ N ≤ 1000,1 ≤ M ≤ 10000。

分析

图的问题,判断一个点能够抵达的点数加上被抵达的点数是不是图上所有的点
比较适合用邻接表来存储
dfs()每次找到一个没有找过的点,就从这个点继续找下去,并且用数组knows[i][j]=1表示i能知道j
遍历每个点,依次dfs()
最后判断整个knows[i][j]来计数知道所有点的点的个数

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
const int maxx = 1001;
vector<int >G[maxx];
int n,m;
int knows[maxx][maxx];
bool vis[maxx];
int top;
void dfs(int x)
{
    knows[top][x]=1;knows[x][top]=1;vis[x]=true;
    for(int i = 0;i<G[x].size();i++)
    {
        if(vis[G[x][i]]==false)
        {
            dfs(G[x][i]);
        }
    }

}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        int a,b;

        while(m--)
        {
            scanf("%d%d",&a,&b);
            G[a].push_back(b);

        }
        for(int i = 1;i<=n;i++)
        {
            for(int j =1;j<=n;j++)
            {
                knows[i][j]=0;

            }
        }
        int cnt = 0;
        for(int i = 1;i<=n;i++)
        {
            for(int j = 0;j<=n;j++)vis[j]=false;
            top = i;
            dfs(i);
        }
        bool flag = true;
        for(int i = 1;i<=n;i++)
        {
            flag = true;
            for(int j = 1;j<=n;j++)
            {
                if(knows[i][j]==0)
                {
                    flag = false;break;
                }
            }
            if(flag)cnt ++;
        }

        printf("%d\n",cnt);
    }
}

分蛋糕

描述

问题描述
  小明今天生日,他有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";
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值