1. 程式人生 > 其它 >CCF-CSP 2016-2018

CCF-CSP 2016-2018

技術標籤:new

目錄

CCF-CSP-2016-4-1 折點計數

CCF-CSP-2016-4-2 俄羅斯方塊

CCF-CSP-2016-9-1 最大波動

CCF-CSP-2016-9-2 火車購票

CCF-CSP-2016-12-1 中間數

CCF-CSP-2016-12-2 工資計算

CCF-CSP-2016-12-4 壓縮編碼

CCF-CSP-2017-3-1 分蛋糕

CCF-CSP-2017-3-2 學生排隊

CCF-CSP-2017-3-4 地鐵修建(結構體優先佇列)

CCF-CSP-2017-9-1 打醬油

CCF-CSP-2017-12-1 最小差值

CCF-CSP-2017-12-2 遊戲

CCF-CSP-2018-3-1 跳一跳

CCF-CSP-2018-3-2 碰撞的小球

CCF-CSP-2018-3-3 URL對映

CCF-CSP-2018-3-4 棋局評估


CCF-CSP-2016-4-1 折點計數

題目:

問題描述

  給定n個整數表示一個商店連續n天的銷售量。如果某天之前銷售量在增長,而後一天銷售量減少,則稱這一天為折點,反過來如果之前銷售量減少而後一天銷售量增長,也稱這一天為折點。其他的天都不是折點。如下圖中,第3天和第6天是折點。

  給定n個整數a1, a2, …, an表示銷售量,請計算出這些天總共有多少個折點。
  為了減少歧義,我們給定的資料保證:在這n天中相鄰兩天的銷售量總是不同的,即ai-1≠ai。注意,如果兩天不相鄰,銷售量可能相同。

輸入格式

  輸入的第一行包含一個整數n。
  第二行包含n個整數,用空格分隔,分別表示a1, a2, …, an。

輸出格式

  輸出一個整數,表示折點出現的數量。

樣例輸入

7
5 4 1 2 3 6 4

樣例輸出

2

評測用例規模與約定

  所有評測用例滿足:1 ≤ n ≤ 1000,每天的銷售量是不超過10000的非負整數。

程式碼:

#include <iostream>
using namespace std;
 
int main()
{
	int a, b, c, ans = 0;
	cin >> a >> a >> b;
	while (cin >> c)
	{
		ans += (b > a && b > c) + (b < a && b < c);
		a = b, b = c;
	}
	cout << ans;
	return 0;
}

CCF-CSP-2016-4-2 俄羅斯方塊

題目:

問題描述

  俄羅斯方塊是俄羅斯人阿列克謝·帕基特諾夫發明的一款休閒遊戲。
  遊戲在一個15行10列的方格圖上進行,方格圖上的每一個格子可能已經放置了方塊,或者沒有放置方塊。每一輪,都會有一個新的由4個小方塊組成的板塊從方格圖的上方落下,玩家可以操作板塊左右移動放到合適的位置,當板塊中某一個方塊的下邊緣與方格圖上的方塊上邊緣重合或者達到下邊界時,板塊不再移動,如果此時方格圖的某一行全放滿了方塊,則該行被消除並得分。
  在這個問題中,你需要寫一個程式來模擬板塊下落,你不需要處理玩家的操作,也不需要處理消行和得分。
  具體的,給定一個初始的方格圖,以及一個板塊的形狀和它下落的初始位置,你要給出最終的方格圖。

輸入格式

  輸入的前15行包含初始的方格圖,每行包含10個數字,相鄰的數字用空格分隔。如果一個數字是0,表示對應的方格中沒有方塊,如果數字是1,則表示初始的時候有方塊。輸入保證前4行中的數字都是0。
  輸入的第16至第19行包含新加入的板塊的形狀,每行包含4個數字,組成了板塊圖案,同樣0表示沒方塊,1表示有方塊。輸入保證板塊的圖案中正好包含4個方塊,且4個方塊是連在一起的(準確的說,4個方塊是四連通的,即給定的板塊是俄羅斯方塊的標準板塊)。
  第20行包含一個1到7之間的整數,表示板塊圖案最左邊開始的時候是在方格圖的哪一列中。注意,這裡的板塊圖案指的是16至19行所輸入的板塊圖案,如果板塊圖案的最左邊一列全是0,則它的左邊和實際所表示的板塊的左邊是不一致的(見樣例)

輸出格式

  輸出15行,每行10個數字,相鄰的數字之間用一個空格分隔,表示板塊下落後的方格圖。注意,你不需要處理最終的消行。

樣例輸入

0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 1 0 0 0
1 1 1 0 0 0 1 1 1 1
0 0 0 0 1 0 0 0 0 0
0 0 0 0
0 1 1 1
0 0 0 1
0 0 0 0
3

樣例輸出

0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 1 0 0 0
1 1 1 1 1 1 1 1 1 1
0 0 0 0 1 1 0 0 0 0

程式碼:

#include <iostream>
using namespace std;
 
int list[16][10], b[4][4], k, loc = 0;
 
bool ok(int r)
{
	for (int i = r; i < r + 4; i++)for (int j = k - 1; j < k + 3; j++)if (list[i][j] * b[i-r][j-k+1])return false;
	return true;
}
 
int main()
{
	for (int i = 0; i < 15; i++)for (int j = 0; j < 10; j++)cin >> list[i][j];
	for (int j = 0; j < 10; j++)list[15][j] = 1;
	for (int i = 0; i < 4; i++)for (int j = 0; j < 4; j++)cin >> b[i][j];
	cin >> k;
	for (int i = 1; i <= 14 && ok(i); i++)loc = i;
	for (int i = loc; i < loc + 4; i++)for (int j = k - 1; j < k + 3; j++)list[i][j] += b[i-loc][j-k+1];
	for (int i = 0; i < 15; i++)
	{
		for (int j = 0; j < 10; j++)cout << list[i][j] << " ";
		cout << endl;
	}
	return 0;
}

CCF-CSP-2016-9-1 最大波動

題目:

問題描述

  小明正在利用股票的波動程度來研究股票。小明拿到了一隻股票每天收盤時的價格,他想知道,這隻股票連續幾天的最大波動值是多少,即在這幾天中某天收盤價格與前一天收盤價格之差的絕對值最大是多少。

輸入格式

  輸入的第一行包含了一個整數n,表示小明拿到的收盤價格的連續天數。
  第二行包含n個正整數,依次表示每天的收盤價格。

輸出格式

  輸出一個整數,表示這隻股票這n天中的最大波動值。

樣例輸入

6
2 5 5 7 3 5

樣例輸出

4

樣例說明

  第四天和第五天之間的波動最大,波動值為|3-7|=4。

評測用例規模與約定

  對於所有評測用例,2 ≤ n ≤ 1000。股票每一天的價格為1到10000之間的整數。

程式碼:

#include <iostream>
using namespace std;
 
int main()
{
	int n, a, b, ans = 0;
	cin >> n >> a;
	while (--n)
	{
		cin >> b;
		if (ans < a - b)ans = a - b;
		if (ans < b - a)ans = b - a;
		a = b;
	}
	cout << ans;
	return 0;
}

CCF-CSP-2016-9-2 火車購票

題目:

問題描述

  請實現一個鐵路購票系統的簡單座位分配演算法,來處理一節車廂的座位分配。
  假設一節車廂有20排、每一排5個座位。為方便起見,我們用1到100來給所有的座位編號,第一排是1到5號,第二排是6到10號,依次類推,第20排是96到100號。
  購票時,一個人可能購一張或多張票,最多不超過5張。如果這幾張票可以安排在同一排編號相鄰的座位,則應該安排在編號最小的相鄰座位。否則應該安排在編號最小的幾個空座位中(不考慮是否相鄰)。
  假設初始時車票全部未被購買,現在給了一些購票指令,請你處理這些指令。

輸入格式

  輸入的第一行包含一個整數n,表示購票指令的數量。
  第二行包含n個整數,每個整數p在1到5之間,表示要購入的票數,相鄰的兩個數之間使用一個空格分隔。

輸出格式

  輸出n行,每行對應一條指令的處理結果。
  對於購票指令p,輸出p張車票的編號,按從小到大排序。

樣例輸入

4
2 5 4 2

樣例輸出

1 2
6 7 8 9 10
11 12 13 14
3 4

樣例說明

  1) 購2張票,得到座位1、2。
  2) 購5張票,得到座位6至10。
  3) 購4張票,得到座位11至14。
  4) 購2張票,得到座位3、4。

評測用例規模與約定

  對於所有評測用例,1 ≤ n ≤ 100,所有購票數量之和不超過100。

程式碼:

#include <iostream>
using namespace std;
 
int main()
{
	int num[20], n, a;
	for (int i = 0; i < 20; i++)num[i] = 5;
	cin >> n;
	while (n--)
	{
		cin >> a;
		for (int i = 0; i < 20 && a; i++)if (num[i] >= a)
		{
			for (int j = i * 5 + 6 - num[i]; j < i * 5 + 6 - num[i] + a; j++)cout << j << " ";
			num[i] -= a;
			a = 0;
		}
		while (a)for (int i = 0; i < 20; i++)if (num[i])
		{
			for (int j = i * 5 + 6 - num[i]; j <= i * 5 + 5; j++)cout << j << " ";
			a -= num[i];
			num[i] = 0;
		}
		cout << endl;
	}
	return 0;
}

CCF-CSP-2016-12-1 中間數

題目:

問題描述

  在一個整數序列a1, a2, …, an中,如果存在某個數,大於它的整數數量等於小於它的整數數量,則稱其為中間數。在一個序列中,可能存在多個下標不相同的中間數,這些中間數的值是相同的。
  給定一個整數序列,請找出這個整數序列的中間數的值。

輸入格式

  輸入的第一行包含了一個整數n,表示整數序列中數的個數。
  第二行包含n個正整數,依次表示a1, a2, …, an。

輸出格式

  如果約定序列的中間數存在,則輸出中間數的值,否則輸出-1表示不存在中間數。

樣例輸入

6
2 6 5 6 3 5

樣例輸出

5

樣例說明

  比5小的數有2個,比5大的數也有2個。

樣例輸入

4
3 4 6 7

樣例輸出

-1

樣例說明

  在序列中的4個數都不滿足中間數的定義。

樣例輸入

5
3 4 6 6 7

樣例輸出

-1

樣例說明

  在序列中的5個數都不滿足中間數的定義。

評測用例規模與約定

  對於所有評測用例,1 ≤ n ≤ 1000,1 ≤ ai≤ 1000。

程式碼:

#include <iostream>
#include<algorithm>
using namespace std;
 
int list[1001];
 
int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)cin >> list[i];
	sort(list, list + n);
	int mid = list[n / 2], num = 0;
	for (int i = 0; i < n; i++)if (list[i] == mid)num++;
	if (n % 2 != num % 2)mid = -1;
	cout << mid;
	return 0;
}

CCF-CSP-2016-12-2 工資計算

題目:

問題描述

  小明的公司每個月給小明發工資,而小明拿到的工資為交完個人所得稅之後的工資。假設他一個月的稅前工資(扣除五險一金後、未扣稅前的工資)為S元,則他應交的個人所得稅按如下公式計算:
  1) 個人所得稅起徵點為3500元,若S不超過3500,則不交稅,3500元以上的部分才計算個人所得稅,令A=S-3500元;
  2) A中不超過1500元的部分,稅率3%;
  3) A中超過1500元未超過4500元的部分,稅率10%;
  4) A中超過4500元未超過9000元的部分,稅率20%;
  5) A中超過9000元未超過35000元的部分,稅率25%;
  6) A中超過35000元未超過55000元的部分,稅率30%;
  7) A中超過55000元未超過80000元的部分,稅率35%;
  8) A中超過80000元的部分,稅率45%;
  例如,如果小明的稅前工資為10000元,則A=10000-3500=6500元,其中不超過1500元部分應繳稅1500×3%=45元,超過1500元不超過4500元部分應繳稅(4500-1500)×10%=300元,超過4500元部分應繳稅(6500-4500)×20%=400元。總共繳稅745元,稅後所得為9255元。
  已知小明這個月稅後所得為T元,請問他的稅前工資S是多少元。

輸入格式

  輸入的第一行包含一個整數T,表示小明的稅後所得。所有評測資料保證小明的稅前工資為一個整百的數。

輸出格式

  輸出一個整數S,表示小明的稅前工資。

樣例輸入

9255

樣例輸出

10000

評測用例規模與約定

  對於所有評測用例,1 ≤ T ≤ 100000。

程式碼:

#include <iostream>
using namespace std;
 
int f(int t)
{
	if (t <= 3500)return t;
	int s = 3500;
	t -= 3500;
	if (t <= 1455)return s + t * 100 / 97;
	s += 1500, t -= 1455;
	if (t <= 2700)return s + t * 100 / 90;
	s += 3000, t -= 2700;
	if (t <= 3600)return s + t * 100 / 80;
	s += 4500, t -= 3600;
	if (t <= 19500)return s + t * 100 / 75;
	s += 26000, t -= 19500;
	if (t <= 14000)return s + t * 100 / 70;
	s += 20000, t -= 14000;
	if (t <= 16250)return s + t * 100 / 65;
	s += 25000, t -= 16250;
	return s + t * 100 / 55;
}
 
int main()
{
	int t;
	cin >> t;
	cout << f(t);
	return 0;
}

CCF-CSP-2016-12-4 壓縮編碼

題目:

問題描述

  給定一段文字,已知單詞a1, a2, …, an出現的頻率分別t1, t2, …, tn。可以用01串給這些單詞編碼,即將每個單詞與一個01串對應,使得任何一個單詞的編碼(對應的01串)不是另一個單詞編碼的字首,這種編碼稱為字首碼。
  使用字首碼編碼一段文字是指將這段文字中的每個單詞依次對應到其編碼。一段文字經過字首編碼後的長度為:
  L=a1的編碼長度×t1+a2的編碼長度×t2+…+ an的編碼長度×tn。
  定義一個字首編碼為字典序編碼,指對於1 ≤ i < n,ai的編碼(對應的01串)的字典序在ai+1編碼之前,即a1, a2, …, an的編碼是按字典序升序排列的。
  例如,文字E A E C D E B C C E C B D B E中, 5個單詞A、B、C、D、E出現的頻率分別為1, 3, 4, 2, 5,則一種可行的編碼方案是A:000, B:001, C:01, D:10, E:11,對應的編碼後的01串為1100011011011001010111010011000111,對應的長度L為3×1+3×3+2×4+2×2+2×5=34。
  在這個例子中,如果使用哈夫曼(Huffman)編碼,對應的編碼方案是A:000, B:01, C:10, D:001, E:11,雖然最終文字編碼後的總長度只有33,但是這個編碼不滿足字典序編碼的性質,比如C的編碼的字典序不在D的編碼之前。
  在這個例子中,有些人可能會想的另一個字典序編碼是A:000, B:001, C:010, D:011, E:1,編碼後的文字長度為35。
  請找出一個字典序編碼,使得文字經過編碼後的長度L最小。在輸出時,你只需要輸出最小的長度L,而不需要輸出具體的方案。在上面的例子中,最小的長度L為34。

輸入格式

  輸入的第一行包含一個整數n,表示單詞的數量。
  第二行包含n個整數,用空格分隔,分別表示a1, a2, …, an出現的頻率,即t1, t2, …, tn。請注意a1, a2, …, an具體是什麼單詞並不影響本題的解,所以沒有輸入a1, a2, …, an。

輸出格式

  輸出一個整數,表示文字經過編碼後的長度L的最小值。

樣例輸入

5
1 3 4 2 5

樣例輸出

34

樣例說明

  這個樣例就是問題描述中的例子。如果你得到了35,說明你算得有問題,請自行檢查自己的演算法而不要懷疑是樣例輸出寫錯了。

評測用例規模與約定

  對於30%的評測用例,1 ≤ n ≤ 10,1 ≤ ti≤ 20;
  對於60%的評測用例,1 ≤ n ≤ 100,1 ≤ ti≤ 100;
  對於100%的評測用例,1 ≤ n ≤ 1000,1 ≤ ti≤ 10000。

在寫動態規劃的備忘錄方法時需要注意一點,答案的最大值很接近接近10^8,而不是10^7

在我的程式碼中,我用m來表示這個數

程式碼:

#include <iostream>
using namespace std;
 
int list[1000], ans[1000][1000], m = 100000000, s[1000];
 
int f(int l, int r)
{
	if (l == r)return 0;
	if (ans[l][r] < m)return ans[l][r];
	for (int i = l; i < r; i++)
	{
		int result = f(l, i) + f(i+1, r) + s[r] - s[l] + list[l];
		if (ans[l][r]>result)ans[l][r] = result;
	}
	return ans[l][r];
}
 
int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)cin >> list[i];
	s[0] = list[0];
	for (int i = 1; i < n; i++)s[i] = s[i - 1] + list[i];
	for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)ans[i][j] = m;
	cout << f(0, n - 1);
	return 0;
}

CCF-CSP-2017-3-1 分蛋糕

題目:

問題描述

  小明今天生日,他有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。

程式碼:

#include <iostream>
using namespace std;
 
int main()
{
	int n, k, ans = 0, s = 0, a;
	cin >> n >> k;
	while (n--)
	{
		cin >> a;
		s += a;
		if (s >= k)
		{
			s = 0;
			ans++;
		}
	}
	if (s)ans++;
	cout << ans;
	return 0;
}

CCF-CSP-2017-3-2 學生排隊

題目:

問題描述

  體育老師小明要將自己班上的學生按順序排隊。他首先讓學生按學號從小到大的順序排成一排,學號小的排在前面,然後進行多次調整。一次調整小明可能讓一位同學出隊,向前或者向後移動一段距離後再插入佇列。
  例如,下面給出了一組移動的例子,例子中學生的人數為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,所有移動均合法。

程式碼:

#include <iostream>
using namespace std;
 
int main()
{
	int n, m, list[1001], loc[1001], p, q;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)list[i] = i, loc[i] = i;
	while (m--)
	{
		cin >> p >> q;
		int d = q > 0 ? 1 : -1;
		for (int i = loc[p]; i != loc[p] + q; i += d)
		{
			list[i] = list[i + d];
			loc[list[i]] = i;
		}
		list[loc[p] + q] = p;
		loc[p] += q;
	}
	for (int i = 1; i < n; i++)cout << list[i] << " ";
	cout << list[n];
	return 0;
}

CCF-CSP-2017-3-4 地鐵修建(結構體優先佇列)

題目:

問題描述

  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號樞紐可以通過隧道到達其他所有樞紐。

程式碼:

#include <iostream>
#include<queue>
#include<vector>
using namespace std;
 
int n, m;
bool visit[100001];
struct dist
{
	int id;
	int len;
};
dist d, d2;
queue<dist>q[100001];
dist ans[100001];
struct cmp
{
	bool operator()(dist a, dist b)
	{
		return a.len > b.len;
	}
};
priority_queue<dist, vector<dist>, cmp>que;
 
int main()
{
	
	int a, b, c;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)while (!q[i].empty())q[i].pop();
	for (int i = 1; i <= n; i++)
	{
		ans[i].id = i;
		ans[i].len = 1000000;
	}
	ans[1].len = 0;
	while (!que.empty())que.pop();
	for (int i = 1; i <= n; i++)que.push(ans[i]);
	for (int i = 1; i <= n; i++)visit[i] = false;
	while (m--)
	{
		scanf("%d%d%d", &a, &b, &c);
		d.id = b, d.len = c;
		q[a].push(d);
		d.id = a;
		q[b].push(d);
	}
	while (!que.empty())
	{
		d = que.top();
		que.pop();
		if (d.len > ans[d.id].len)continue;
		if (visit[d.id])continue;
		visit[d.id] = true;
		while (!q[d.id].empty())
		{
			d2 = q[d.id].front();
			q[d.id].pop();
			if (visit[d2.id])continue;
			if (d2.len < ans[d.id].len)d2.len = ans[d.id].len;
			if (ans[d2.id].len>d2.len)ans[d2.id].len = d2.len;
			que.push(d2);
		}
	}
	cout << ans[n].len;
	return 0;
}

使用結構體優先佇列的時候需要注意,修改結構體的成員變數優先佇列並不會自動維護最大(小)堆,為了解決這個問題,我使用的方法是將修改後的結構體變數又插入優先佇列,而在優先佇列彈出一個元素時,直接把它和ans進行對比即可知道它是不是被修改的最後一個版本,如果不是最後一個版本的話,彈出它就行了,不進行任何操作,也不標記visit

所以,整個程式的複雜度是O(n+m),n是頂點數,m是邊數

CCF-CSP-2017-9-1 打醬油

問題描述

  小明帶著N元錢去買醬油。醬油10塊錢一瓶,商家進行促銷,每買3瓶送1瓶,或者每買5瓶送2瓶。請問小明最多可以得到多少瓶醬油。

輸入格式

  輸入的第一行包含一個整數N,表示小明可用於買醬油的錢數。N是10的整數倍,N不超過300。

輸出格式

  輸出一個整數,表示小明最多可以得到多少瓶醬油。

樣例輸入

40

樣例輸出

5

樣例說明

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

樣例輸入

80

樣例輸出

11

樣例說明

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

程式碼:

#include<iostream>
using namespace std;
 
int main()
{
	int n;
	cin >> n;
	cout << n / 10 + n / 50 * 2 + n % 50 / 30;
	return 0;
}

CCF-CSP-2017-12-1 最小差值

題目:

問題描述

  給定n個數,請找出其中相差(差的絕對值)最小的兩個數,輸出它們的差值的絕對值。

輸入格式

  輸入第一行包含一個整數n。
  第二行包含n個正整數,相鄰整數之間使用一個空格分隔。

輸出格式

  輸出一個整數,表示答案。

樣例輸入

5
1 5 4 8 20

樣例輸出

1

樣例說明

  相差最小的兩個數是5和4,它們之間的差值是1。

樣例輸入

5
9 3 6 1 3

樣例輸出

0

樣例說明

  有兩個相同的數3,它們之間的差值是0.

資料規模和約定

  對於所有評測用例,2 ≤n≤ 1000,每個給定的整數都是不超過10000的正整數。

程式碼:

#include<iostream>
#include<algorithm>
using namespace std;
 
int main()
{
	int n, num[1000], ans = 10000;
	cin >> n;
	for (int i = 0; i < n; i++)cin >> num[i];
	sort(num, num + n);
	for (int i = 0; i < n - 1; i++)
		if (ans > num[i + 1] - num[i])ans = num[i + 1] - num[i];
	cout << ans;
	return 0;
}

CCF-CSP-2017-12-2 遊戲

題目:

問題描述

  有n個小朋友圍成一圈玩遊戲,小朋友從1至n編號,2號小朋友坐在1號小朋友的順時針方向,3號小朋友坐在2號小朋友的順時針方向,……,1號小朋友坐在n號小朋友的順時針方向。
  遊戲開始,從1號小朋友開始順時針報數,接下來每個小朋友的報數是上一個小朋友報的數加1。若一個小朋友報的數為k的倍數或其末位數(即數的個位)為k,則該小朋友被淘汰出局,不再參加以後的報數。當遊戲中只剩下一個小朋友時,該小朋友獲勝。
  例如,當n=5, k=2時:
  1號小朋友報數1;
  2號小朋友報數2淘汰;
  3號小朋友報數3;
  4號小朋友報數4淘汰;
  5號小朋友報數5;
  1號小朋友報數6淘汰;
  3號小朋友報數7;
  5號小朋友報數8淘汰;
  3號小朋友獲勝。

  給定n和k,請問最後獲勝的小朋友編號為多少?

輸入格式

  輸入一行,包括兩個整數n和k,意義如題目所述。

輸出格式

  輸出一行,包含一個整數,表示獲勝的小朋友編號。

樣例輸入

5 2

樣例輸出

3

樣例輸入

7 3

樣例輸出

4

資料規模和約定

  對於所有評測用例,1 ≤n≤ 1000,1 ≤k≤ 9。

暴力

程式碼:

#include<iostream>
using namespace std;
 
int main()
{
	int n, k, s, ans[1001];
	cin >> n >> k;
	s = n;
	for (int i = 1; i <= n; i++)ans[i] = i;
	for (int i = 1, key = 1; s > 1; i++, key++)
	{
		if (key > s)key = 1;
		if (i%k == 0 || i % 10 == k)
		{
			for (int j = key + 1; j <= s; j++)ans[j - 1] = ans[j];
			s--, key--;
		}
	}
	cout << ans[1];
	return 0;
}

CCF-CSP-2018-3-1 跳一跳

題目:

問題描述

  近來,跳一跳這款小遊戲風靡全國,受到不少玩家的喜愛。
  簡化後的跳一跳規則如下:玩家每次從當前方塊跳到下一個方塊,如果沒有跳到下一個方塊上則遊戲結束。
  如果跳到了方塊上,但沒有跳到方塊的中心則獲得1分;跳到方塊中心時,若上一次的得分為1分或這是本局遊戲的第一次跳躍則此次得分為2分,否則此次得分比上一次得分多兩分(即連續跳到方塊中心時,總得分將+2,+4,+6,+8...)。
  現在給出一個人跳一跳的全過程,請你求出他本局遊戲的得分(按照題目描述的規則)。

輸入格式

  輸入包含多個數字,用空格分隔,每個數字都是1,2,0之一,1表示此次跳躍跳到了方塊上但是沒有跳到中心,2表示此次跳躍跳到了方塊上並且跳到了方塊中心,0表示此次跳躍沒有跳到方塊上(此時遊戲結束)。

輸出格式

  輸出一個整數,為本局遊戲的得分(在本題的規則下)。

樣例輸入

1 1 2 2 2 1 1 2 2 0

樣例輸出

22

資料規模和約定

  對於所有評測用例,輸入的數字不超過30個,保證0正好出現一次且為最後一個數字。

程式碼:

#include<iostream>
using namespace std;
 
int main()
{
	int t, r = 0, s = 0;
	while (cin >> t && t > 0)if (t == 1)r = 1, s++; else r += 2 - r % 2, s += r;
	cout << s;
	return 0;
}

CCF-CSP-2018-3-2 碰撞的小球

題目:

問題描述

  數軸上有一條長度為L(L為偶數)的線段,左端點在原點,右端點在座標L處。有n個不計體積的小球線上段上,開始時所有的小球都處在偶數座標上,速度方向向右,速度大小為1單位長度每秒。
  當小球到達線段的端點(左端點或右端點)的時候,會立即向相反的方向移動,速度大小仍然為原來大小。
  當兩個小球撞到一起的時候,兩個小球會分別向與自己原來移動的方向相反的方向,以原來的速度大小繼續移動。
  現在,告訴你線段的長度L,小球數量n,以及n個小球的初始位置,請你計算t秒之後,各個小球的位置。

提示

  因為所有小球的初始位置都為偶數,而且線段的長度為偶數,可以證明,不會有三個小球同時相撞,小球到達線段端點以及小球之間的碰撞時刻均為整數。
  同時也可以證明兩個小球發生碰撞的位置一定是整數(但不一定是偶數)。

輸入格式

  輸入的第一行包含三個整數n, L, t,用空格分隔,分別表示小球的個數、線段長度和你需要計算t秒之後小球的位置。
  第二行包含n個整數a1, a2, …, an,用空格分隔,表示初始時刻n個小球的位置。

輸出格式

  輸出一行包含n個整數,用空格分隔,第i個整數代表初始時刻位於ai的小球,在t秒之後的位置。

樣例輸入

3 10 5
4 6 8

樣例輸出

7 9 9

樣例說明

  初始時,三個小球的位置分別為4, 6, 8。

  一秒後,三個小球的位置分別為5, 7, 9。

  兩秒後,第三個小球碰到牆壁,速度反向,三個小球位置分別為6, 8, 10。

  三秒後,第二個小球與第三個小球在位置9發生碰撞,速度反向(注意碰撞位置不一定為偶數),三個小球位置分別為7, 9, 9。

  四秒後,第一個小球與第二個小球在位置8發生碰撞,速度反向,第三個小球碰到牆壁,速度反向,三個小球位置分別為8, 8, 10。

  五秒後,三個小球的位置分別為7, 9, 9。

樣例輸入

10 22 30
14 12 16 6 10 2 8 20 18 4

樣例輸出

6 6 8 2 4 0 4 12 10 2

資料規模和約定

  對於所有評測用例,1 ≤ n ≤ 100,1 ≤ t ≤ 100,2 ≤ L ≤ 1000,0 < ai < L。L為偶數。
  保證所有小球的初始位置互不相同且均為偶數。

思路:

每次2個小球碰撞,都向相反的方向運動,可以考慮成,2個球直接互相穿過,互不影響。

這樣,所有的球都是各自獨立運動,可以直接求出每個球最後的位置。

最後只需要把最開始的球和最後的球進行匹配即可,因為球和球之間是左右順序是不會變的,

所以(只需要把最後所有球的位置排序)根據這個指標就可以直接求出來。

程式碼:

#include<iostream>
#include<algorithm>
using namespace std;
 
int main()
{
	int n, l, t, loc[101], ans[101], temp;
	cin >> n >> l >> t;
	t %= l * 2;
	for (int i = 1; i <= n; i++)cin >> loc[i];
	for (int i = 1; i <= n; i++)
	{
		ans[i] = 1;
		for (int j = 1; j <= n; j++)if (loc[i] > loc[j])ans[i]++;
	}
	for (int i = 1; i <= n; i++)
	{
		if (t < l - loc[i])loc[i] += t;
		else
		{
			temp = t;
			t -= l - loc[i];
			if (t < l)loc[i] = l - t;
			else loc[i] = t - l;
			t = temp;
		}
	}
	sort(loc + 1, loc + n + 1);
	for (int i = 1; i <= n; i++)cout << loc[ans[i]] << ((i < n) ? ' ' : '\n');
	return 0;
}

CCF-CSP-2018-3-3 URL對映

題目:

問題描述

  URL 對映是諸如 Django、Ruby on Rails 等網頁框架 (web frameworks) 的一個重要元件。對於從瀏覽器發來的 HTTP 請求,URL 對映模組會解析請求中的 URL 地址,並將其分派給相應的處理程式碼。現在,請你來實現一個簡單的 URL 對映功能。
  本題中 URL 對映功能的配置由若干條 URL 對映規則組成。當一個請求到達時,URL 對映功能會將請求中的 URL 地址按照配置的先後順序逐一與這些規則進行匹配。當遇到第一條完全匹配的規則時,匹配成功,得到匹配的規則以及匹配的引數。若不能匹配任何一條規則,則匹配失敗。
  本題輸入的 URL 地址是以斜槓 / 作為分隔符的路徑,保證以斜槓開頭。其他合法字元還包括大小寫英文字母、阿拉伯數字、減號 -、下劃線 _ 和小數點 .。例如,/person/123/ 是一個合法的 URL 地址,而 /person/123? 則不合法(存在不合法的字元問號 ?)。另外,英文字母區分大小寫,因此 /case/ 和 /CAse/ 是不同的 URL 地址。
  對於 URL 對映規則,同樣是以斜槓開始。除了可以是正常的 URL 地址外,還可以包含引數,有以下 3 種:
  字串 <str>:用於匹配一段字串,注意字串裡不能包含斜槓。例如,abcde0123。
  整數 <int>:用於匹配一個不帶符號的整數,全部由阿拉伯數字組成。例如,01234。
  路徑 <path>:用於匹配一段字串,字串可以包含斜槓。例如,abcd/0123/。
  以上 3 種引數都必須匹配非空的字串。簡便起見,題目規定規則中 <str> 和 <int> 前面一定是斜槓,後面要麼是斜槓,要麼是規則的結束(也就是該引數是規則的最後一部分)。而 <path> 的前面一定是斜槓,後面一定是規則的結束。無論是 URL 地址還是規則,都不會出現連續的斜槓。

輸入格式

  輸入第一行是兩個正整數n和m,分別表示 URL 對映的規則條數和待處理的 URL 地址個數,中間用一個空格字元分隔。
  第 2 行至第n+1 行按匹配的先後順序描述 URL 對映規則的配置資訊。第i+1 行包含兩個字串pi和ri,其中pi表示 URL 匹配的規則,ri表示這條 URL 匹配的名字。兩個字串都非空,且不包含空格字元,兩者中間用一個空格字元分隔。
  第n+2 行至第n+m+1 行描述待處理的 URL 地址。第n+1+i行包含一個字串qi,表示待處理的 URL 地址,字串中不包含空格字元。

輸出格式

  輸入共m行,第i行表示qi的匹配結果。如果匹配成功,設匹配了規則pj,則輸出對應的rj。同時,如果規則中有引數,則在同一行內依次輸出匹配後的引數。注意整數引數輸出時要把前導零去掉。相鄰兩項之間用一個空格字元分隔。如果匹配失敗,則輸出 404。

樣例輸入

5 4
/articles/2003/ special_case_2003
/articles/<int>/ year_archive
/articles/<int>/<int>/ month_archive
/articles/<int>/<int>/<str>/ article_detail
/static/<path> static_serve
/articles/2004/
/articles/1985/09/aloha/
/articles/hello/
/static/js/jquery.js

樣例輸出

year_archive 2004
article_detail 1985 9 aloha
404
static_serve js/jquery.js

樣例說明

  對於第 1 個地址 /articles/2004/,無法匹配第 1 條規則,可以匹配第 2 條規則,引數為 2004。
  對於第 2 個地址 /articles/1985/09/aloha/,只能匹配第 4 條規則,引數依次為 1985、9(已經去掉前導零)和 aloha。
  對於第 3 個地址 /articles/hello/,無法匹配任何一條規則。
  對於第 4 個地址 /static/js/jquery.js,可以匹配最後一條規則,引數為 js/jquery.js。

資料規模和約定

  1 ≤n≤ 100,1 ≤m≤ 100。
  所有輸入行的長度不超過 100 個字元(不包含換行符)。
  保證輸入的規則都是合法的。

程式碼:

#include<iostream>
#include<string>
using namespace std;
 
bool f(string s1, string s2,bool flag)
{
	int k1 = 0, k2 = 0, len1 = s1.length(), len2 = s2.length();
	while (k1 < len1 && k2 < len2)
	{
		if (s1[k1] == s2[k2])
		{
			k1++, k2++;
			continue;
		}
		if (s1[k1++] != '<')return false;
		if (flag)cout << ' ';
		if (s1[k1] == 'i')
		{
			bool flag2 = false;
			while (s2[k2] && s2[k2] >= '0' && s2[k2] <= '9')
			{
				if (s2[k2] > '0')flag2 = true;
				if (flag2 && flag)cout << s2[k2];
				k2++;
			}
			if (!flag2)return false;
			k1 += 4;
			continue;
		}
		if (s1[k1] == 's')
		{
			bool flag2 = false;
			while (s2[k2] && s2[k2] != '/')
			{
				flag2 = true;
				if (flag)cout << s2[k2];
				k2++;
			}
			if (!flag2)return false;
			k1 += 4;
			continue;
		}
		if (s1[k1] == 'p')
		{
			if (flag)while (s2[k2])cout << s2[k2++];
			return true;
		}
	}
	return (k1 == len1 && k2 == len2);
}
 
int main()
{
	int n, m;
	cin >> n >> m;
	string sn[100], name[100], s;
	for (int i = 0; i < n; i++)cin >> sn[i] >> name[i];
	for (int i = 0; i < m; i++)
	{
		cin >> s;
		bool flag = true;
		for (int i = 0; i < n && flag; i++)if (f(sn[i], s,false))
		{
			flag = false;
			cout << name[i];
			f(sn[i], s, true);
		}
		if (flag)cout << "404";
		cout << endl;
	}
	return 0;
}

熱心網友給出了輸入用例,當<int>匹配的是0時本程式會匹配失敗,這是一個bug

輸入用例:

1 1
/<int>/ aa
/0/

CCF-CSP-2018-3-4 棋局評估

題目:

  Alice和Bob正在玩井字棋遊戲。
  井字棋遊戲的規則很簡單:兩人輪流往3*3的棋盤中放棋子,Alice放的是“X”,Bob放的是“O”,Alice執先。當同一種棋子佔據一行、一列或一條對角線的三個格子時,遊戲結束,該種棋子的持有者獲勝。當棋盤被填滿的時候,遊戲結束,雙方平手。
  Alice設計了一種對棋局評分的方法:
  - 對於Alice已經獲勝的局面,評估得分為(棋盤上的空格子數+1);
  - 對於Bob已經獲勝的局面,評估得分為 -(棋盤上的空格子數+1);
  - 對於平局的局面,評估得分為0;


  例如上圖中的局面,Alice已經獲勝,同時棋盤上有2個空格,所以局面得分為2+1=3。
  由於Alice並不喜歡計算,所以他請教擅長程式設計的你,如果兩人都以最優策略行棋,那麼當前局面的最終得分會是多少?

輸入格式

  輸入的第一行包含一個正整數T,表示資料的組數。
  每組資料輸入有3行,每行有3個整數,用空格分隔,分別表示棋盤每個格子的狀態。0表示格子為空,1表示格子中為“X”,2表示格子中為“O”。保證不會出現其他狀態。
  保證輸入的局面合法。(即保證輸入的局面可以通過行棋到達,且保證沒有雙方同時獲勝的情況)
  保證輸入的局面輪到Alice行棋。

輸出格式

  對於每組資料,輸出一行一個整數,表示當前局面的得分。

樣例輸入

3
1 2 1
2 1 2
0 0 0
2 1 1
0 2 1
0 0 2
0 0 0
0 0 0
0 0 0

樣例輸出

3
-4
0

樣例說明

  第一組資料:
  Alice將棋子放在左下角(或右下角)後,可以到達問題描述中的局面,得分為3。
  3為Alice行棋後能到達的局面中得分的最大值。
  第二組資料:


  Bob已經獲勝(如圖),此局面得分為-(3+1)=-4。
  第三組資料:
  井字棋中若雙方都採用最優策略,遊戲平局,最終得分為0。

資料規模和約定

  對於所有評測用例,1 ≤T≤ 5。

程式碼:

#include<iostream>
#include<string.h>
using namespace std;
 
int a[9],ans[3][3][3][3][3][3][3][3][3];
 
int isend()
{
	for (int i = 0; i < 3; i++)if (a[i] == a[i + 3] && a[i] == a[i + 6] && a[i])return a[i];
	for (int i = 0; i < 9; i += 3)if (a[i] == a[i + 1] && a[i] == a[i + 2] && a[i])return a[i];
	if (a[0] == a[4] && a[4] == a[8] && a[4])return a[4];
	if (a[2] == a[4] && a[4] == a[6] && a[4])return a[4];
	return 0;
}
 
int dp()
{
	if (ans[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]]< 100)
		return ans[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]];
	int s = 0;
	for (int i = 0; i < 9; i++)if (a[i])s++;
	if (isend() == 1)return 10 - s;
	if (isend() == 2)return s - 10;
	if (s == 9)return 0;
	int r;
	if (s % 2)
	{
		r = 100;
		for (int i = 0; i < 9; i++)
		{
			if (a[i])continue;
			a[i] = 2;
			if (r > dp())r = dp();
			a[i] = 0;
		}
	}
	else
	{
		r = -100;
		for (int i = 0; i < 9; i++)
		{
			if (a[i])continue;
			a[i] = 1;
			if (r < dp())r = dp();
			a[i] = 0;
		}
	}
	ans[a[0]][a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]] = r;
	return r;
}
 
int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		memset(ans, 1, sizeof(ans));
		for (int i = 0; i < 9; i++)cin >> a[i];
		cout << dp() << endl;
	}
	return 0;
}