2017 交大競技程式訓練冬令營
Day1
Outline
- 輸入與輸出
- 演算法的複雜度
- 二分搜尋演算法
- 排序演算法
輸入與輸出
C++中兩大類輸入方式
-
stdio
-
iostream
stdio
scanf / printf
#include <cstdio>
int main(){
int a;
long long b;
unsigned long long c;
char s[100];
double d;
scanf("%d-%lld-%llu %s %lf",
&a, &b, &c, s, &d);
printf("%10d\n", a);
printf("%10lld\n", b);
printf("%10llu\n", c);
printf("%10s\n", s);
printf("%10f\n", d);
}
// input
1-2-3 abc 7.122
//output
1
2
3
abcd
7.122000
符號 | 內容 | 範例 |
---|---|---|
%i %d | 整數 | -7122 |
%u | 無號整數 | 7122 |
%f | 浮點數 | 71.22 |
%e %E | 科學記號(E是大寫) | 7.122e+4 / 7.122E+4 |
%c | 字元 |
J |
%s | 字串 | JIXX |
%% | %字元 | % |
常用scanf/printf 符號表
gets / puts
#include <stdio.h>
int main(){
char s [256];
gets(s);
puts(s);
}
//input
1 2 3 4 5 6 7 8 9 10 11
//output
1 2 3 4 5 6 7 8 9 10 11
fgets / fputs
#include <stdio.h>
int main(){
char s [256];
fgets(s, 256, stdin);
fputs(s, stdout);
}
為了防止溢位
現代人都應該改用fgets!!!
getchar / putchar
#include <stdio.h>
int main(){
char c;
c = getchar();
putchar(c);
}
輸入優化 - 用 getchar 輸入整數
template <class T>
bool input(T& a){
a=(T)0;
register char p;
while ((p = getchar()) < '-')
if (p==0 || p==EOF) return false;
if (p == '-')
while ((p = getchar()) >= '0') a = a*10 - (p^'0');
else {
a = p ^ '0';
while ((p = getchar()) >= '0') a = a*10 + (p^'0');
}
return true;
}
template<typename T, typename ...Args>
inline bool input(T &x, Args &...args){
return input(x)&&input(args...);
}
追求更快的輸入優化
char readchar(){
static const int bufsize = 1<<16;
static char buf[bufsize], *p=buf, *e=buf;
return (p==e &&
(e=(p=b)+fread(buf,1,bufsize,stdin),p==e)?0:*p++);
}
更快的 getchar()!!!
iostream
std::cin / std::cout
#include <iostream>
using namespace std;
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
cout << n << endl;
return 0;
}
cin/cout 由編譯器幫你判斷型別,省去判斷型別的時間和麻煩
ios_base::sycn_with_stdio
cin/cout 平常為了和stdio同步,所以速度會受到影響,關閉同步期望可以讓 cin/cout 的速度變快
把同步關掉之後,同時使用stdio 和 iostream 會發生不可預期的問題,請特別注意
cin.tie(0)
cin/cout 平常是互相綁定的,呼叫 cin 就會自動清空 cout 的緩衝區
cin.tie(0) 將會讓 cin / cout 解除綁定,但是要注意 <<endl 依然會清空緩衝區,必須改成 <<'\n' 才會有優化的效果
當需要輸出大量的行數的時候會差很多
std::getline - 輸入 string
#include <iostream>
#include <string>
int main ()
{
std::string name;
std::cout << "Please, enter your name: ";
std::getline(std::cin, name);
std::cout << "Hello, " << name << "!\n";
return 0;
}
std::istream::getline()
#include <iostream>
int main () {
char name[256],;
std::cout << "Please, enter your name: ";
std::cin.getline (name,256);
std::cout << "Hello, " << name << "!\n";
return 0;
}
Example
輸入 T 筆測資
int main(){
int t;
scanf("%d",&t);
while(t--){
solve();
}
}
int main(){
int t;
cin>>t;
while(t--){
solve();
}
}
以 0 結尾的測資
int main(){
while ( scanf("%d",&n) && n!=0 ){
solve(n);
}
}
int main(){
while ( cin>>n && n!=0 ){
solve(n);
}
}
讀到 EOF(檔案結尾字元)的測資
int main(){
while ( ~scanf("%d %d %d", &a, &b ,&c) ){
solve(a,b,c);
}
}
int main(){
while ( cin>>a>>b>>c ){
solve(a,b,c);
}
}
不知道一行有幾個數字的測資
#include <cstdio>
#include <cstring>
int main(){
char s[256];
while ( fgets(s,256,stdin) != NULL ){
char *ptr = strtok(s," \n");
int cnt=0, a;
do{
sscanf(ptr, "%d", &a)
cnt++;
ptr = strtok(NULL," \n");
}while (ptr!=NULL);
printf("%d\n",cnt);
}
}
不知道一行有幾個數字的測資
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main(){
string s;
while(getline(cin, s)){
stringstream ss(s);
int cnt=0, a;
while(ss>>a)cnt++;
cout<<cnt<<endl;
}
}
輸出到小數點後第3位(四捨五入)
#include <cstdio>
int main(){
float pi = 3.1415926;
printf("%.3f\n", pi); //3.142
}
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
float pi = 3.1415926;
cout<<fixed<<setprecision(3);
cout<<pi<<endl; //3.142
}
輸出一個5位數整數,不足補0
#include <cstdio>
int main(){
int n = 7122;
printf("%05d\n", n); //07122
}
#include <iostream>
#include <iomanip>
using namespace std;
int main(){
int n = 7122;
cout<<setfill('0');
cout<<setw(5)<<n<<endl; //07122
}
時間複雜度
如何估計程式跑多快?
Big O
描述函數漸進行為的數學符號
Big O
\forall n
∀n
then\ f(n) = O(g(n))
then f(n)=O(g(n))
if\ \exists\ c,C
if ∃ c,C
s.t.\ n > C\ and\ |f(n)| \leq |cg(n)|
s.t. n>C and ∣f(n)∣≤∣cg(n)∣
-
O(100n) 等價 O(n)
-
O(nlgn+n) 等價 O(nlgn)
-
O(50n^2+10n+100) 等價 O(n^2)
常見的時間複雜度
- O(nlgn)
- O(n)
- O(lgn)
- O(2^n)
- O(n+Qlgn)
- O(1)
- ...
偷偷告訴你,現在電腦一般來說一秒可以跑 5*10^8
有些10^7、有些10^9
二分搜尋法
如何快速地找東西?
一個一個找 - O(N)
如果東西已經
按照大小排好了呢?
二分搜尋法
先找中間
每次直接把搜尋範圍砍一半
Binary Search
#include <stdio.h>
const int INF = 1<<29;
bool ok(int x){
if (x>=100)return 1;
else return 0;
}
int main(){
int l=0, r=INF;
while (l!=r){
int mid = (l+r)/2;
if ( ok(mid) )r=mid;
else l=mid+1;
}
printf("%d\n",l);
}
尋找一個數字(以100為例)
時間分析
假設範圍為 C
每次範圍會縮小一半
所以總共要搜尋 lg C 次
複雜度就是 O(lg C)
排序演算法
常見的排序演算法
- 氣泡排序法(bubble sort)
- 選擇排序法(selection sort)
- 插入排序法(insertion sort)
- 快速排序法(quick sort)
- 合併排序法(merge sort)
- 堆排序法(heap sort)
氣泡排序法
讓最輕的東西像氣泡一樣浮上來的排序法
(雖然實作時好像都是讓最重的沉下去)
需要兩層迴圈所以是 O(N^2)
Bubble Sort
int bubble_sort(int a[], int n){
for (int i=0; i<n ;i++){
for (int j=0; j<n-1; j++){
if (a[j] > a[j+1]) swap(a[j],a[j+1]);
}
}
}
選擇排序法
選一個最小的拿出來
選N次就可以把N個東西排好序
因為要選N次
每次都要全部看過才知道誰最小
所以是 O(N^2)
Selection Sort
int selection_sort(int a[], int n){
for (int i=0; i<n; i++){
for (int j=i+1; j<n; j++){
if (a[i] > a[j]) swap(a[i],a[j]);
}
}
}
插入排序法
每次拿一個東西
然後插入到他應該在的位置
(平常我整理書櫃都用這個方法XD)
把東西插入他應該在的位置需要看過一遍
還要把後面的東西往後挪
所以 O(N^2)
Insertion Sort
int insertion_sort(int a[], int n){
for (int i=0; i<n; i++){
for (int j=i; j>0; j--){
if (a[j-1] > a[j]) swap(a[j-1],a[j]);
else break;
}
}
}
快速排序法
聽起來就很快的排序法(?
隨機找一個數字放在中間
把大於他的放右邊,小於他的放左邊
然後遞迴
T(N) = 2*T(N/2) + O(N) = O(nlgn)
Quick Sort
void quick_sort(int l, int r, int a[]){
if (l>=r) return;
int p = l;
for (int i=l+1; i<=r; i++){
if (a[i] < a[p]) swap(a[++p], a[i]);
}
swap(a[l], a[p]);
quick_sort(l,p-1,a);
quick_sort(p+1,r,a);
}
合併排序法
運用分治法(Divide and Conquer)
把序列切成一半,各別排好序
用O(N)合併兩個排好序的結果
也就是比較兩個序列的頭來合併
T(N) = 2*T(N/2) + O(N) = O(NlgN)
Merge Sort
void merge_sort(int l, int r, int a[], int tmp[]){
if (l>=r) return;
int mid = (l+r)/2;
merge_sort(l,mid,a,tmp);
merge_sort(mid+1,r,a,tmp);
int p=mid+1, q=l;
for(int i=l; i<=mid; i++){
while(p<=r && a[i]>a[p])tmp[q++] = a[p++];
tmp[q++] = a[i];
}
while(p<=r)tmp[q++] = a[p++];
for(int i=l; i<=r; i++)a[i] = tmp[i];
}
堆排序法
把序列變成一個Heap
(Heap 是一個可以找最大值的資料結構)
再依序把東西從Heap裡拿出來
有點像選擇排序法
但是把找最大值的時間變成O(NlgN)了
其中 Heap的原理有點像氣泡排序法
只是從一條直鍊變成一顆完全二元樹
時間為 N*O(NlgN) = O(N lgN)
Heap Sort
void heap_sort(int a[], int n){
for(int i=0; i<n; i++){
int w=i;
while(w>0){
if(a[(w-1)/2]<a[w])std::swap(a[(w-1)/2],a[w]);
w/=2;
}
}
for (int m=n-1; m>0; m--){
std::swap(a[0],a[m]);
int w=0;
while (1){
int tw=w;
if (w*2+1<m && a[w*2+1]>a[tw])tw=w*2+1;
if (w*2+2<m && a[w*2+2]>a[tw])tw=w*2+2;
if (tw==w)break;
std::swap(a[w],a[tw]);
w=tw;
}
}
}
交大程式設計冬令營-Day1
By samsam2310
交大程式設計冬令營-Day1
- 1,167