CCF-CSP 201512-4 送貨 100分(解決RE)

  發現網上不少代碼(包括一些號稱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的交叉路口出發,每次必須沿街道去往街道另外一端的路口,再重新的路口出發去往下一個路口,直到全部的街道都通過了正好一次。
輸入格式
  輸入的第一行包含兩個整數 nm,表示交叉路口的數量和街道的數量,交叉路口從1到 n標號。
  接下來 m行,每行兩個整數 ab,表示和標號爲 a的交叉路口和標號爲 b的交叉路口之間有一條街道,街道是雙向的,小明能夠從任意一端走向另外一端。兩個路口之間最多有一條街道。
輸出格式
  若是小明能夠通過每條街道正好一次,則輸出一行包含 m+1個整數 p 1p 2p 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 }
相關文章
相關標籤/搜索