發現網上不少代碼(包括一些號稱100分的代碼)都存在一些技術問題,導致提交以後出現RE而且只有80分,所以寫了這篇筆記。ios
針對這些代碼的問題進行了簡單的分類:算法
1. .bss段溢出。靜態分配了規模爲MxN=1e9 B≈1e3 MB的二維數來存儲鄰接矩陣(題目所給出的是稠密圖),這顯然是有問題的。最坑的是,在DevC++/MinGW環境下,儘管.bss段已經溢出了,代碼仍然能夠經過編譯,編譯器不給出任何錯誤或警告,程序也能在輸入數據規模較小的狀況下正常運行,這使得不注意預估規模時容易出問題。函數
2. 遞歸調用過深,致使爆棧(棧空間溢出)。由於程序調用棧是動態的,這個問題一樣很是隱蔽,關鍵是難以預估系統爲咱們分配了多大的棧空間。通過實測,在DevC++/MinGW環境下,系統爲程序分配的堆棧空間大約只有128KiB,本題中dfs函數遞歸調用32496幀(1e4)後程序就崩潰了,遞歸的深度是在程序拋出異常後經過gdb觀察到的。spa
綜上解決辦法以下:設計
針對問題1,改用鄰接表或鏈式前向星存儲圖便可;指針
針對問題2,有兩種解決方案:一是改進算法,修改成顯式用棧的DFS;二是手動擴棧,相比於傳統的#pragma,本文將介紹一種通用性很強的內聯彙編擴棧方法,關於該方法的原理在往後會詳細介紹(若是有必要的話)。code
先把原題貼在這裏,方便查閱:對象
試題編號: | 201512-4 |
試題名稱: | 送貨 |
時間限制: | 1.0s |
內存限制: | 256.0MB |
問題描述: |
問題描述
爲了增長公司收入,F公司新開設了物流業務。因爲F公司在業界的良好口碑,物流業務一開通即受到了消費者的歡迎,物流業務立刻遍佈了城市的每條街道。然而,F公司如今只安排了小明一我的負責全部街道的服務。
任務雖然繁重,可是小明有足夠的信心,他拿到了城市的地圖,準備研究最好的方案。城市中有 n個交叉路口, m條街道鏈接在這些交叉路口之間,每條街道的首尾都正好鏈接着一個交叉路口。除開街道的首尾端點,街道不會在其餘位置與其餘街道相交。每一個交叉路口都至少鏈接着一條街道,有的交叉路口可能只鏈接着一條或兩條街道。 小明但願設計一個方案,從編號爲1的交叉路口出發,每次必須沿街道去往街道另外一端的路口,再重新的路口出發去往下一個路口,直到全部的街道都通過了正好一次。
輸入格式
輸入的第一行包含兩個整數
n,
m,表示交叉路口的數量和街道的數量,交叉路口從1到
n標號。
接下來 m行,每行兩個整數 a, b,表示和標號爲 a的交叉路口和標號爲 b的交叉路口之間有一條街道,街道是雙向的,小明能夠從任意一端走向另外一端。兩個路口之間最多有一條街道。
輸出格式
若是小明能夠通過每條街道正好一次,則輸出一行包含
m+1個整數
p
1,
p
2,
p
3, ...,
pm
+1,表示小明通過的路口的順序,相鄰兩個整數之間用一個空格分隔。若是有多種方案知足條件,則輸出字典序最小的一種方案,即首先保證
p
1最小,
p
1最小的前提下再保證
p
2最小,依此類推。
若是不存在方案使得小明通過每條街道正好一次,則輸出一個整數-1。
樣例輸入
4 5
1 2 1 3 1 4 2 4 3 4
樣例輸出
1 2 4 1 3 4
樣例說明
城市的地圖和小明的路徑以下圖所示。
樣例輸入
4 6
1 2 1 3 1 4 2 4 3 4 2 3
樣例輸出
-1
樣例說明
城市的地圖以下圖所示,不存在知足條件的路徑。
評測用例規模與約定
前30%的評測用例知足:1 ≤
n ≤ 10,
n-1 ≤
m ≤ 20。
前50%的評測用例知足:1 ≤ n ≤ 100, n-1 ≤ m ≤ 10000。 全部評測用例知足:1 ≤ n ≤ 10000, n-1 ≤ m ≤ 100000。 |
本文僅在以前代碼的基礎上,手動調用malloc分配了一塊虛存,而後經過內聯彙編直接修改ESP/RSP棧指針寄存器,將棧空間指向新分配的虛擬內存,從而改變當前棧空間。固然事情尚未這麼簡單,當main函數返回時,程序實際上會回溯最原始的運行時庫中去,原始的棧空間中仍有數據,但此時咱們新指定的棧已經空了,若是不還原棧指針寄存器的地址爲運行時庫分配的地址,會致使棧失去平衡,從而使程序崩潰,所以咱們事先將原始棧指針保存到一個變量中,在main函數return以前恢復棧指針的值便可實現無縫銜接。blog
具體實現位於源程序Line 73~82和Line 149~153。這裏須要利用預處理宏__i386__區分當前處理器體系是x86仍是x86_64,由於32bit體系使用ESP存儲棧指針,而64bit體系使用RSP。除非預先知道OJ使用的系統字長,不然應當保守處理,將針對兩種體系的版本都寫好。遞歸
1 /* 2 * 歐拉路徑問題 3 * 4 * 無向圖存在歐拉路徑的充要條件是: 5 * 1. 圖各個頂點連通(是連通圖) 6 * 2. 各頂點的度都爲偶數,或者至多存在兩個頂點度爲奇數(則這兩個頂點爲起點或終點) 7 * 8 * 所以先利用並查集判斷圖中各個頂點是否都連通,而後判斷有無度爲奇數的頂點,若是有,則這些點要麼是起點要麼是終點。 9 * 當符合歐拉路徑的條件時,依題意直接從頂點1開始進行DFS(窮舉的對象是邊,不是頂點) 10 * 便可找出歐拉路徑。假如題目換一下,不能肯定起點爲1,應該考慮fleury算法。 11 * 12 * 本題給出的圖爲稠密圖(M<=1e5),直接DFS出現棧溢出錯誤。簡單的解決方案是手工分配更大的棧空間(malloc), 13 * 而後經過修改ESP或RSP寄存器使得當前棧指向新分配的空間。這裏須要注意當main函數返回以前,應當恢復以前的 14 * 棧指針寄存器,不然會形成返回到crt時棧不平衡。 15 */ 16 #include <iostream> 17 #include <vector> 18 #include <stack> 19 #include <queue> 20 #include <algorithm> 21 22 #include <fstream> 23 24 #define N 10000+2 25 #define M 100000+2 26 27 using namespace std; 28 29 struct edge { 30 int u, m; 31 32 inline bool operator<(const edge &e) const { 33 return u < e.u; 34 } 35 }; 36 37 static vector<edge> con[N]; 38 static int deg[N]; 39 static int parent[N]; 40 41 static char vism[M]; 42 static stack<int> path; 43 44 static int uf_find(int a) { 45 if (parent[a] == a) 46 return a; 47 else 48 return (parent[a] = uf_find(parent[a])); 49 } 50 51 static inline void uf_union(int a, int b) { 52 int pa = uf_find(a); 53 int pb = uf_find(b); 54 if (pa != pb) { 55 parent[pa] = pb; 56 } 57 } 58 59 static void search(int cur) { 60 61 for(size_t i=0; i<con[cur].size(); ++i) { 62 const edge &e = con[cur][i]; 63 if (!vism[e.m]) { 64 vism[e.m] = 1; 65 search(e.u); 66 } 67 } 68 69 path.push(cur); 70 } 71 72 int main(void) { 73 register char *sp; 74 int size = 10 << 20; // 10MB 75 char *p = (char*)malloc(size) + size; 76 #ifdef __i386__ 77 __asm__("movl %%esp, %0\n" :: "r"(sp)); 78 __asm__("movl %0, %%esp\n" :: "r"(p)); 79 #else 80 __asm__("mov %%rsp, %0\n" :: "r"(sp)); 81 __asm__("mov %0, %%rsp\n" :: "r"(p)); 82 #endif 83 ios::sync_with_stdio(false); 84 85 fstream fin("input1.txt"); 86 cin.rdbuf(fin.rdbuf()); 87 88 int n,m; 89 cin >> n >> m; 90 91 for(int i=1; i<=n; ++i) { 92 parent[i] = i; 93 } 94 for(int i=0; i<m; ++i) { 95 int a,b; 96 edge e; 97 cin >> a >> b; 98 e.m = i; 99 e.u = b; 100 con[a].push_back(e); 101 e.u = a; 102 con[b].push_back(e); 103 ++deg[a]; ++deg[b]; 104 uf_union(a,b); 105 } 106 107 bool conn = true; 108 int p1 = uf_find(1); 109 for(int i=2;i<=n; ++i) { 110 int pi = uf_find(i); 111 if (pi != p1) { 112 conn = false; 113 break; 114 } 115 } 116 117 if (!conn) { 118 cout << -1 << endl; 119 } else { 120 vector<int> start; 121 int odd = 0; 122 for(int i=1; i<=n; ++i) { 123 sort(con[i].begin(), con[i].end()); 124 if (deg[i] & 1) { 125 ++odd; 126 start.push_back(i); 127 } 128 } 129 130 if (odd <= 2 && find(start.begin(), start.end(), 1)!=start.end()) { 131 search(1); 132 133 while(!path.empty()) { 134 int t = path.top(); 135 path.pop(); 136 cout << t; 137 if (path.empty()) { 138 cout << endl; 139 } else { 140 cout << " "; 141 } 142 } 143 144 } else { 145 cout << -1 << endl; 146 } 147 } 148 149 #ifdef __i386__ 150 __asm__("movl %0, %%esp\n" :: "r"(sp)); 151 #else 152 __asm__("mov %0, %%rsp\n" :: "r"(sp)); 153 #endif 154 return 0; 155 }