CCF 2017 真題 持續更新ing
公共鑰匙盒
描述
問題描述
有一個學校的老師共用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";
}
}