【持續更新】【pat】pat刷題筆記

修改code completion快捷鍵位CTRL+ENTER,幫助提示函數名稱

修改命令行提示符的屬性,開啓快速編輯模式,方便調試

添加c++11語言標準支持

開啓代碼調試功能

對輸入的字符串進行切割時,可以使用scanf按照指定格式分別輸入達到切割效果,比如:

//對於這樣的輸入
3-10 99
11-5 87
102-1 0
//對於這種格式不絕對統一的字符串,可以自己構造相應的scanf分割對應的數據
scanf("%d-%d %d", &t, &num, &score);    

對於需要進行除法運算的變量,並且有精確度要求的時候,可以這樣

double grade =0;
//其中fullscore數組爲int類型,在*1.0小數之後,自動轉換爲浮點型
grade += fullscore[j] * 1.0 / 2;
//或者這樣強轉
grade += (double)fullscore[j]/2;

如果在getline讀取行之前還有其它的輸入字符(不管是字符還是整數),則需要保證在調用getline函數之前,使用getchar()函數讀取一次回車符,然後再調用

#include<iostream>
using namespace std;
int main() {
	char n;
	scanf("%c",&n);
	getchar();
	string str;
	getline(cin,str);
	cout<<str;
}

散列表的應用:在需要進行遍歷數組內容,判斷某一個元素是否存在時,可以使用一個大數組,初始化爲0,錄入數據時,相應數據(把下標看成數據)的內容修改爲1,這樣判斷的時候直接判斷a[element]==1即可,其中element爲錄入的數據。這樣就把O(n)級別的時間複雜度降到了O(1)

atoi&stoi(stol)&strtol區別

參考:https://www.ibm.com/developerworks/community/blogs/12bb75c9-dfec-42f5-8b55-b669cc56ad76/entry/c_11%25e4%25b8%25ad%25e7%259a%2584string_atoi_itoa?lang=en

即使不記得也沒關係,只要記住以下用法即可

//數字型變量轉字符串
to_string();//c++全局函數

//字符串轉數字
stoi();//to int
stol();//to long 
stof();//to float
//都需要導入#include <string>     

四捨五入,可以先double +0.5然後轉int取整數部分,也可以用cmath的函數round()

數組,字符串或其它常用stl容器大規模清零或者填充數據可以使用fill()函數

// fill algorithm example
#include <iostream>     // std::cout
#include <algorithm>    // std::fill
#include <vector>       // std::vector

int main () {
  std::vector<int> myvector (8);                       // myvector: 0 0 0 0 0 0 0 0

  std::fill (myvector.begin(),myvector.begin()+4,5);   // myvector: 5 5 5 5 0 0 0 0
  std::fill (myvector.begin()+3,myvector.end()-2,8);   // myvector: 5 5 5 8 8 8 0 0

  std::cout << "myvector contains:";
  for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}

output

myvector contains: 5 5 5 8 8 8 0 0

將十進制a轉換爲b進制數,當a不爲0時,將a%b從後往前倒序保存下來,每次保存後將a/b。這樣倒序保存的數就是十進制a在b進制下的結果。

進制互轉代碼參考如下

#include<iostream>
#include<cstdlib>
using namespace std;
int main(){
    //把8進制的17轉化爲10進制打印輸出
    string str = "17";
    char *tmp ;
    long result = strtol(str.c_str(),&tmp,8);
    cout<<result;
    return 0;
}

#include<iostream>
#include<algorithm>
using namespace std;
//digital爲10進制數,r爲需要轉換的目標進制,返回目標進制數
string dtox(int digital,int r){
    string result="";
    const char s[37]="0123456789abcdefghijklmnopqrstuvwxyz";
    if(digital==0){
        return "0";
    }
    while(digital!=0){
        int tmp =digital%r;
        result+=s[tmp];
        digital/=r;
    }
    reverse(result.begin(),result.end());
    return result;
}
int main(){
    cout<<"十進制10轉爲16進制結果:"<<dtox(10,16)<<endl;
    cout<<"十進制10轉爲8進制結果:"<<dtox(10,8)<<endl;
    cout<<"十進制10轉爲2進制結果:"<<dtox(10,2)<<endl;
    cout<<"十進制10轉爲10進制結果:"<<dtox(10,10)<<endl;
}

儘量使用c++的string類

讀取整行的代碼如下

string str;
getline(cin,str);//該函數在std標準庫中,不需要引入string頭文件

c++的string類可以轉化爲c的字符數組,str.c_str();

最大公約數和最小公倍數求解

int gcd(int a,int b){
    return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b){
    return a*b/gcd(a,b);
}

對於一些可以事先進行預處理然後用散列表來求解的問題,可以大大減少時間複雜度。比如一個需要大量查詢斐波那契數列的問題中,我們可以事先把前N個斐波那契數列求解出來然後放到散列表中進行保存,之後的查詢就都是O(1)的時間複雜度。

▲多利用位運算來求解交集,並集,差集,可以大大減少時間複雜度,降低編碼難度。

素數的判斷

bool isPrime(int n){
    if(n <= 1) return false;
    int sqr = (int)sqrt(1.0*n);
    for(int i = 2;i<=sqr; i++){
        if(n%i==0) return false;
    }
    return true;
}

常用C++ STL容器

C++ container

algorithm頭文件常用函數

algorithm

  • max
  • min
  • abs
  • swap
  • reverse
  • is_permutation
  • next_permutation
  • prev_permutation
  • fill
  • sort
  • lower_bound
  • upper_bound

大數組必須定義爲全局變量

字符數組要多開一個單位

string.find()返回的是下標的值,沒有找到用==string::npos

dev c++調試代碼時,查看vector容器的內容 查看vector容器的內容

同時存在邊權和點權的最短路徑問題,求最短路徑條數以及最大點權和打印最短路徑

  • 用一遍Dijkstra算法即可
  • dis[i]表示從出發點到i結點最短路徑的路徑長度
  • num[i]表示從出發點到i結點最短路徑的條數
  • w[i]表示從出發點到i結點點權的數目之和
  • path[i]表示從出發點到i結點的前驅結點,利用棧(或者遞歸)打印路徑即可

圖的初始化

  • 如果是無向圖,需要注意初始化邊的時候,是有兩條邊的,不能漏掉了

  • 沒有邊的情況(包括兩種情況,1.頂點v,w之間不存在邊,2.頂點v本身沒有到達自己的邊),初始化爲0,如果是初始化爲-1,則判斷邊是否存在的時候需要注意G[i][j]<0而不是直接if(G[i][j]),這兩種判斷一個是判斷0,一個是判斷<0

  • 圖的初始化,可以用fill函數,具體用法可以參考如下

    //必須添加algorithm頭文件
    #include<algorithm>
    
    //在內存地址區間[first,last)範圍內,填充x
    fill(first,last,x);
    
    //但是特別注意二維數組的填充
    int arr[2][2];
    fill(arr[0],arr[0]+4,0);
    /*
    這裏需要注意的是,arr[0]表示的纔是整個二維數組的起始地址,而不是arr,另外,這個區間是左閉右開區間!!!
    */
    

    不僅是圖的初始化, 其它的輔助數組也可以用fill函數初始化,比如說path數組(用於記錄頂點w的前驅頂點,打印最短路徑),dist數組(用於記錄出發點到頂點w的最短路徑),num數組(用於記錄從出發點到頂點w的最短路徑條數),w數組(用於記錄從出發點到頂點w的點權之和)等。

  • 在真正進行Dijkstra算法之前,先檢查下面這些數據是否都已經初始化好了

    1.初始化圖
    2.初始化路徑
    3.初始化距離
    4.初始化收錄情況
    5.初始化點
    6.初始化出發點,以及出發點的鄰接點的路徑和距離信息
    

最短路徑擴展問題

  • 要求數最短路徑有多少條

    • count[s] = 1;
    • 如果找到更短路:count[W]=count[V];
    • 如果找到等長路:count[W]+=count[V];
  • 要求邊數最少的最短路

    • count[s] = 0;
    • 如果找到更短路:count[W]=count[V]+1;
    • 如果找到等長路:count[W]=count[V]+1;
  • 存在點權

    比如救火問題,多條最短路徑,選擇點權最大的那條

    for(int i = 0; i < n; i++) {
    	int u = -1, minn = inf;
    	for(int j = 0; j < n; j++) {
    		if(visit[j] == false && dis[j] < minn) {
    			u = j;
    			minn = dis[j];
    		}
    	}
    	if(u == -1) break;
    	visit[u] = true;
    	for(int v = 0; v < n; v++) {
    		if(visit[v] == false && e[u][v] != inf) {
    			if(dis[u] + e[u][v] < dis[v]) {
    				dis[v] = dis[u] + e[u][v];
    				num[v] = num[u];
    				w[v] = w[u] + weight[v];
    			} else if(dis[u] + e[u][v] == dis[v]) {
    				num[v] = num[v] + num[u];
    				if(w[u] + weight[v] > w[v])
    					w[v] = w[u] + weight[v];
    			}
    		}
    	}
    }
    
  • 邊權不唯一

    比如旅遊規劃問題,存在多條最短路徑時,選擇花費最少的

    void Dijkstra( Vertex s ) {
    	while (1) {
    		V = 未收錄頂點中dist最小者;
    		if ( 這樣的V不存在)
    			break;
    		collected[V] = true;
    		for ( V 的每個鄰接點W )
    			if ( collected[W] == false )
    				if ( dist[V]+E<V,W> < dist[W] ) {
    					dist[W] = dist[V] + E<V,W> ;
    					path[W] = V;
    					cost[W] = cost[V] + C<V,W> ;
    				} else if ( (dist[V]+E<V,W> == dist[W])
    				            && (cost[V]+C<V,W> < cost[W]) ) {
    					cost[W] = cost[V] + C<V,W> ;
    					path[W] = V;
    				}
    	}
    }
    

algorithm常用函數補充 所有的range都是左閉右開區間 //判斷range1是否爲range2(長度length2>=length1)的子序列,可以完全相等 is_permutation(first1,last1,first2);

//查找range2在range1中的位置 search(first1,last1,first2,last2);

//交換a和b的值 swap(a,b);

//填充range fill(first,last,value);

//reverse反轉一個range reverse(first,last);

//判斷range是否有序【升序asc】 is_sorted(first,last);

//獲取從那個位置開始無序 is_sorted_until(foo.begin(),foo.end());

//一個有序range插入新元素x的最小插入位置 lower_bound (v.begin(), v.end(), 20);

//一個有序range插入新元素x的最大插入位置 upper_bound (v.begin(), v.end(), 20);

//合併兩個range到一個新容器中 std::merge (first,first+5,second,second+5,v.begin());

//獲取min std::min(1,2)

//獲取max std::max(1,2)

//獲取min_element std::cout << "The smallest element is " << *std::min_element(myints,myints+7) << '\n';

//獲取max_element std::cout << "The largest element is " << *std::max_element(myints,myints+7) << '\n';

//獲取一個range的一個序列 int main () { int myints[] = {1,2,3};

std::sort (myints,myints+3);

std::cout << "The 3! possible permutations with 3 elements:\n"; do { std::cout << myints[0] << ' ' << myints[1] << ' ' << myints[2] << '\n'; } while ( std::next_permutation(myints,myints+3) );//對應的就是更大的序列

std::cout << "After loop: " << myints[0] << ' ' << myints[1] << ' ' << myints[2] << '\n';

return 0; } 輸出結果 The 3! possible permutations with 3 elements: 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 After loop: 1 2 3

類似的還有 std::prev_permutation(myints,myints+3) 返回一個bool true if the function could rearrange the object as a lexicographicaly smaller permutation(字典序更小的子序列). Otherwise, the function returns false to indicate that the arrangement is not less than the previous, but the largest possible (sorted in descending order).

開考前,可以先用記事本把一些常用的頭文件寫下來

注意求平均值的時候是否需要四捨五入,如果需要+0.5即可

排名問題,如果兩個人分數一樣,那麼排名也是一樣的,比如 第一種情況: 1,1,1,4,5,6

stu[0].rank[flag] = 1;
for(int i = 1; i < n; i++) {
	stu[i].rank[flag] = i + 1;
	if(stu[i].score[flag] == stu[i-1].score[flag])
		stu[i].rank[flag] = stu[i-1].rank[flag];
}

第二種情況: 1,2,3,4,5 這種比較簡單,可以直接使用for循環,或者while循環,臨時變量每次自增1即可

通常都是第一種情況,如果是第二種情況的話,很可能會附加排序字段

fill函數對於重置exist,flag,visit等類型的數組很方便,也省時間,一般只在fill函數無法滿足要求時才考慮使用遍歷重置數組

求圖的連通分量的個數

void dfs(int node) {
	visit[node] = true;
	for(int i = 1; i <= n; i++) {
		if(visit[i] == false && v[node][i] == 1)
			dfs(i);
	}
}

int cnt = 0;
for(int j = 1; j <= n; j++) {
    if(visit[j] == false) {
        dfs(j);
        cnt++;
    }
}

還可以用並查集來求連通分量

中序和後序轉先序

#include <cstdio>
using namespace std;
int post[] = {3, 4, 2, 6, 5, 1};
int in[] = {3, 2, 4, 1, 6, 5};
void pre(int root, int start, int end) {
    if(start > end) return ;
    int i = start;
    while(i < end && in[i] != post[root]) i++;
    printf("%d ", post[root]);
    pre(root - 1 - end + i, start, i - 1);
    pre(root - 1, i + 1, end);
}

int main() {
    pre(5, 0, 5);
    return 0;
}

循環輸入

#include<iostream>
using namespace std;
int main(){
	//cin循環輸入測試
	int a;
    //只要輸入的整數a不等於9,循環就會一直執行,直到輸入9
	while(cin>>a,a!=9){
		printf("%d\n",a);
	}
	return 0;
}

如果要在遇到換行符時退出循環,可以使用getchar()函數來判斷

#include<iostream>
using namespace std;
int main() {
	//cin循環輸入測試
	string tkey;
	while(cin >> tkey) {
		cout<<tkey<<endl;
		char c = getchar();
		if(c == '\n') break;
	}
	return 0;
}

原文出處:https://www.cnblogs.com/ericling/p/11888344.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章