劉汝佳《演算法競賽入門經典(第二版)》習題(三)
劉汝佳《演算法競賽入門經典(第二版)》第三章習題(一)
習題3-1 得分(ACM/ICPC Seoul 2005,UVa1585)
給出一個由O和X組成的串(長度為1~80),統計得分。每個O的得分為目前連續出現的O的個數,X的得分為0。例如:OOXXOXXOOO的得分為1+2+0+0+1+0+0+1+2+3。
解析
因為X的得分為0,所以只需要統計O的得分,另外需要注意的是每次遇到X,統計O得分的變數都要置0。
#include <iostream>
#include <cstdio>
using namespace std;
int main (void)
{
string s;
cin >> s;
int onum = 0,sum = 0;
for (int i = 0; i < s.size(); i++)
{
if (s[i] == 'O')
{
onum++;
sum += onum;
}
else if (s[i] == 'X')
onum = 0;
}
cout << sum << endl;
return 0;
}
習題3-2 分子量(ACM/ICPC Seoul 2007,UVa1586)
給出一種物質的分子式(不帶括號),求分子量。本題中的分子式只包含4種原子,分別為 C,H,O,N,原子量分別為12.01,1.008,16.00,14.01(單位:g/mol)。例如:C6H5OH的分子量為94.108g/mol。解析
這道題在ACM/ICPC Seoul 2007中需要輸入的是n(1≤n≤99)個分子量(管他呢),而且並沒有給出資料範圍和執行時間限制,所以暫且當做標準限制來做吧(下面的題目除非書中註明,否則同樣看待)。
因為分子式中數字對應的一定是其前面的分子,所以只需要判斷分子是哪個,以及分子後面的分子數,再根據對應分子的分子量計算就好了。然而麻煩就麻煩在分子數並不一定是個位數,所以我們要怎麼將代表分子數的字串轉換成數字呢?c標準庫提供了atoi,atof,atol,atoll函式(需包含標頭檔案stdlib.h或cstdlib)將字串分別轉換成int,double,long,long long型,我們將兩個分子之間的數字存放在一個字元型陣列,再呼叫atof函式轉換即可。
說起來挺簡單的,但實現起來需要考慮的東西還蠻多。不廢話,上程式碼:
#include <iostream>
#include <cstdlib>
#include <memory>
#include <cctype>
using namespace std;
const char name[] = "CHON";
const double fweight[] = {12.01,1.008,16.00,14.01};
int main (void)
{
char c[5];
string s;
cin >> s;
double sum = 0.0, weight = 0.0;
int num = 0;
for (int i = 0; i < s.size(); i++)//遍歷字串,找出分子
{
if (isalpha(s[i]))
{
for (int j = 0; j < 4; j++)//將分子對應的分子量賦給weight
if (s[i] == name[j])
{
weight = fweight[j];
break;
}
num = 0;//將用來移位的變數清零
if (isalpha(s[i+1]) || i+1 == s.size())//分子式後面若沒有分子數則其分子數預設為1
sum += weight;
else
{
for (int k = i+1; k < s.size() && isdigit(s[k]); k++)//將表示分子數的字串轉換為double型資料
c[num++] = s[k];
sum += atof(c)*weight;//計算分子量
}
memset(c, '\0', sizeof(c));//將用於存放分子數的陣列清空
}
else
continue;
}
cout << sum << "g/mol" << endl;
return 0;
}
習題3-3 數數字(ACM/ICPC Danang 2007,UVa1225)
把前n(n≤10000)個整數順次寫在一起:123456789101112…數一數0~9各出現多少次(輸出10個整數,分別是0,1,…,9出現的次數)。
解析
剛看到這道題的時候我一直以為這些數字的出現是有規律的,想了很久,然而……失敗了(等我用暴搜寫完後在網上找各路大神的解法才悲劇地發現:根本沒有規律T.T)。資料範圍不大,原題的時間限制為3000ms,所以直接暴搜吧。有兩種思路,第一種是對1~n的整數分別進行數字統計,得出結果;第二種是將1~n的整數一個個轉成字串,然後統計,得出結果。
對於第一種思路,一般做法是直接一個1~n的大迴圈,對每個整數一步步拆分,統計數字出現次數,我就是這麼寫的。另外我在網上看到一種比較特別的做法:直接用10000×10的二維陣列做成一個查詢表,再根據輸入的值查詢輸出,下面是1~19的查詢表,幫助理解:
解法一
#include <cstdio>
#include <memory>
#include <cstdlib>
int main (void)
{
int a[10];
int n,num;
scanf ("%d",&n);
memset(a, 0, sizeof(a));
for (int i = 1; i <= n; i++)
{
int temp = i;
while (temp)//拆分整數
{
num = temp%10;
temp /= 10;
a[num]++;
}
}
for (int i = 0; i < 10; i++)
(i == 0)?printf ("%d",a[i]):printf (" %d",a[i]);
return 0;
}
解法二
#include <cstdio>
#include <memory>
#include <cstdlib>
int a[10000][10];
int main (void)
{
memset (a, 0, sizeof(a));
for (int i = 1; i < 10000; i++)
{
for (int j = 0; j < 10; j++)
a[i][j] = a[i-1][j];
for (int k = i; k; k /= 10)
a[i][k%10]++;
}
int n;
scanf ("%d",&n);
for (int j = 0; j < 9; j++)
(j)?printf (" %d",a[n][j]): printf ("%d",a[n][j]);
printf ("\n");
return 0;
}
在本題中解法二相對於解法一在速度上並沒有什麼優勢,但這個製表查詢的想法還是值得學習的(反正我想不到- -)。
對於第二種思路,用sprintf函式(雖然itoa也可以,但與ANSI標準不相容,不建議使用)將每個整數轉成字串是關鍵,直接看程式碼就好了。
解法三
#include <cstdio>
#include <cstdlib>
#include <memory>
#include <cctype>
int main (void)
{
int num[10];
int n;
scanf ("%d",&n);
char c[10];
memset(num, 0, sizeof(num));//初始化存放統計數字的陣列
for (int i = 1; i <= n; i++)//對1~n的數字逐一統計
{
memset(c, '\0', sizeof(c));//初始化存放轉換結果的字元陣列
sprintf(c, "%d", i);//將數字轉換為字元存到陣列c中
for (int j = 0; j < 10; j++)//對字元陣列c進行掃描,對數字進行統計
if (isdigit(c[j]))
{
switch (c[j])
{
case'0':
num[0]++;
break;
case'1':
num[1]++;
break;
case'2':
num[2]++;
break;
case'3':
num[3]++;
break;
case'4':
num[4]++;
break;
case'5':
num[5]++;
break;
case'6':
num[6]++;
break;
case'7':
num[7]++;
break;
case'8':
num[8]++;
break;
case'9':
num[9]++;
break;
}
}
}
for (int i = 0; i < 10; i++)
(i == 0)?printf ("%d",num[i]):printf (" %d",num[i]);
return 0;
}
習題3-4 週期串(UVa 455)
如果一個字串可以由某個長度為k的字串重複多次得到,則稱該串以k為週期。例如,abcabcabcabc以3為週期(注意,它也以6和12為週期)。
輸入一個長度不超過80的字串,輸出其最小週期。
解析
題目已經說得很清楚,做法也很簡單,只需要從1開始假設週期,然後掃描字串的一半,驗證是否是這個週期,是則輸出週期,否則假設的週期增1。
#include <cstdio>
#include <string>
#include <cstdlib>
int main (void)
{
string s;
int kase = 0,T = 0;//kase判斷是否存在週期
cin >> s;
for (int i = 0; i < s.size()/2; i++)//掃描字串的一半
{
T++;
if (s.size()%T == 0)
{
kase = 1;
for (int j = 0; j < T; j++)//驗證假設的第一個週期是否成立
{
for (int k = 0; k+T < s.size(); k += T)
if (s[j] != s[k+j])
{
kase = 0;
break;
}
if (!kase)//一旦發現週期不成立即退出,減少不必要的時間消耗
break;
}
}
if (kase)//同上一個kase作用相同
break;
}
if (kase)
printf ("%d\n",T);
return 0;
}
相關文章
- ACM題解系列之一:劉汝佳:《演算法競賽入門經典》(第2版)ACM演算法
- 演算法競賽入門經典(第2版)-劉汝佳-第三章解題原始碼(C語言)演算法原始碼C語言
- 演算法競賽入門經典(第二版)第二章 習題2-2 韓信點兵演算法
- 初學acmer--《演算法競賽經典入門》第二章2.5 習題自己的解答ACM演算法
- 演算法競賽入門經典訓練指南 pdf演算法
- 演算法競賽入門經典_5 c++與STL入門演算法C++
- 演算法競賽入門經典--紫書6.3.1小球下落演算法
- kaggle再一次入門~經典入門級競賽~Titanic
- 初學acmer--讀《演算法競賽入門經典》筆記(p36-41)ACM演算法筆記
- 演算法競賽C++快速入門演算法C++
- 經典加密演算法入門-RSA加密演算法
- C語言入門經典(第5版)C語言
- Webpack經典入門Web
- 程式語言入門經典案例【Python版】Python
- ACM演算法競賽_快速入門v0.1(施工中)ACM演算法
- 《Flutter 入門經典》之“Flutter 入門 ”Flutter
- [轉]BI入門經典
- Kafka入門經典教程Kafka
- BI入門經典 (轉)
- 聊聊經典機器學習入門機器學習
- [JAVAFX基礎]掃雷經典入門低配版Java
- Spark入門(三)--Spark經典的單詞統計Spark
- 經典演算法題每日演練——第二十四題 梳排序演算法排序
- Unix 入門經典 筆記筆記
- springboot入門經典Spring Boot
- 《HTML與CSS入門經典(第8版)》——2.8作業HTMLCSS
- 《HTML與CSS入門經典(第8版)》——1.6總結HTMLCSS
- 《PHP、MySQL和Apache入門經典(第5版)》一一2.11實踐練習PHPMySqlApache
- [轉自LINUXEDEN]shell入門絕佳!(三)Linux
- 第二屆“演算法控”馬拉松程式設計競賽 解題思路演算法程式設計
- 【Kaggle入門級競賽top5%排名經驗分享】— 建模篇
- 【Kaggle入門級競賽top5%排名經驗分享】— 分析篇
- 經典演算法題每日演練——第二十二題 奇偶排序演算法排序
- C語言演算法競賽入門(二)—陣列元素移動、排序問題、猴子選大王問題C語言演算法陣列排序
- 《HTML與CSS入門經典(第8版)》——1.7問與答HTMLCSS
- 【CSDN競賽第24期】贏熱門圖書《演算法競賽》和定製周邊演算法
- Python入門經典案例一Python
- 《jQueryMobile入門經典》——2.4 總結jQuery