• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            2011年8月21日

            Basic Sample Senario :

            Client 需要一種組件提供一種 FastString類, 此類具有 int length() 方法

            解決方案:

            靜態鏈接 : 組件廠商將源代碼交給 client ,客戶將組件代碼與client 代碼編譯連接運行。 如果組件代碼需要fix bug or update ,則client 端代碼需要重新編譯連接。 而且client軟件的不同實例使用各自的組件內存對象。

            動態鏈接 : 組件廠商使用DLL形式發放組件,此時不同的client實例可以共享組件在內存中的代碼段。

            DLL的問題:1.導出名稱的問題 : 不同的compiler可以使用不同的mangle name 用來區分 c++的函數,那么使用不同的compiler的client和組件無法鏈接 (可以使用extern “C”解決全局函數名的問題,使用 .DEF文件解決導出成員函數的問題)

                               2.升級的問題 :如果組件最初定義為

            class FastString
            {
                char* m_psz;
            public:
                FastString(const char* psz){strcpy(m_psz, psz);}
            };

                                                      而后廠商更改了組件的實現

            class FastString
            {
            int newmember;
            char* psz;
            public:

            FastString(const char* psz){strcpy(m_psz, psz);}

            }

            原來FastString對象大小為4字節,現在變為8字節,但是client端按照4字節分配對象, dll卻要向后面的4個字節存入一個指針,行為不可預料!

            解決這一問題的一種方法便是每次發布便更改dll的名字,即1.0, 2.0, x.0 等等。但這樣比較弱啊!!

            這種問題根本原因是啥呢?

            class 的關鍵在于封裝了其中的實現細節,即用戶知道類提供了哪些服務( public方法)就行了,不需要管類的內部到底使用了哪些成員變量。這樣一來,只要接口沒變(類提供功能),user就可以安心的使用任意版本的實現了。C++怎么不行呢?C++告訴用戶接口的同時,也告訴了用戶類的實現(對象布局)。比如類對象有多大啊,每個成員的偏移啊(具體可以看Inside c++ object model)。知道了這些,客戶端使用接口的代碼就和DLL中的具體實現緊密的耦合起來了,杯具 啊~

            咋辦呢? 只要不讓client直接創建FastString就行了,這樣client的代碼就不會受到FastString實現變化的影響了。給FastString加一個Wrapper類,內部嵌套一個FastString,所有對FastString的調用都foward給內部的FastString member, 創建FastString 的任務在dll方面完成,client只知道Wrapper大小為4個字節--指向FastString的指針。這樣問題解決了,但是太麻煩了,所有的接口都要包一層!! 而且多了一層調用!

            還有啥辦法么? 為了保證c++接口類實現二進制級別的兼容只能使用編譯器無關的特性:1.假設復合類型表現形式相同(struct) 2. 傳參順序相同,可以使用指示符指定3.虛函數調用機制相同,即基于 vtbl 和 vptr. 基于這些假設,我們創建的c++接口類所有函數設置為虛函數,那么不同compiler將為客戶端方法調用產生相同的機器代碼。定義了接口,便規定了所有繼承于他的類的內存結構一定與它兼容。但此時不能告訴用戶類的定義,否則重回上面的老路上了。怎么辦,只有接口客戶無法創建類的定義,只有export一個創建類對象的函數客戶了。  同上面的wrapper一樣,創建類的操作僅僅在dll內部調用,這意味著實際建造類對象大小和布局的代碼與編譯實現類的方法的代碼使用同樣的編譯器創建 (即ctor和call ctor的代碼由同一編譯器同時編譯)。由于虛析構函數在vtbl的位置與compiler相關,所以不能把它設置為虛函數,只有顯示增加一個Delete函數完成析構工作。

            OK,當前我們得到的DLL中只有創建類對象的函數需要用extern “C”export 給客戶,其他的接口中的虛函數是通過虛表訪問的,無需靠符號名字鏈接。

            進一步的,如果我們要給接口增加一個功能呢? 如果直接在現有接口中方法聲明后加入新的方法,那么此方法會出現在vtbl的最后一欄,舊的client不會調用新方法,但是如果新的client訪問老的對象呢? 不幸的事情發生了! 這樣做的問題在于,修改公開的接口就打破了對象的封裝性。

            那么增加接口功能只能通過設計一個接口繼承另一個接口,或者讓類繼承多個接口來實現了。客戶可以在運行時通過RTTI來詢問對象,支持這個功能不,親?然而 ,RTTI也是一個compiler相關的東東,好吧,我們讓每個類自己實現RTTI,也就是實現一個dynamic_cast 方法, 用來將自己cast成為自己實現的接口,如果不支持則返回 0 。

            例如:

            void* CFastString::Dynamic_Cast(const char* pszTypename)
            {
            void * pRev;
            if(strcmp(pszTypename, "IFastString") == 0)
            {
            pRev = static_cast<IFastString*>(this);
            }
            else if(strcmp(pszTypename , "IOut") == 0)
            {
            pRev = static_cast<IOut*>(this);
            }
            else if(strcmp(pszTypename , "IExtent") == 0)
            {
            pRev = static_cast<IFastString*>(this);
            }
            else
            {
            return 0;
            }

            return pRev;
            }

            注意cast到IExtent的時候用了IFastString,因為IFastString 和 IOut都是從IExtent繼承的,寫IExtent的話不知道用哪個,用虛擬繼承可以使CFastString對象只有一份IExtent,為啥不用呢? 你懂得。。。跟前面答案一樣,編譯器相關。

            最后一個問題是delete的問題,用戶需要記得為每一個對象調用一次delete方法,而指針cast來cast去,想記得對象被delete沒有很難啊! 怎么辦? 用引用計數吧,把每個指針當做具有生命周期的實體,創建時候計數++,銷毀時候--,等到0的時候就delete對象。

            大功告成,通過vptr和vtbl的二進制防火墻,我們做到了可重用的二進制組件,組件變化客戶無需重新編譯 。

            posted @ 2011-08-21 16:19 rikisand 閱讀(2486) | 評論 (2)編輯 收藏

            2010年5月26日

            什么時候用memory-mapped files:

            1.System use it to load and execute .exe and dll files.

            2.Use memory-mapped files to access a data file on disk.

            3.allow multiple processes running on the same machine to share data with each other.

            ------------------------------------------------------------------------------------------------------

            用途一:使用mapping file executables and dlls

            當調用createProcess時候,系統做了以下的事情:

            1.locates CreateProcess 中指定的the .exe file,失敗則return false

            2.創建新的process kernel object

            3.creates a private address space for new process

            4.reserves a region of address space large enough to contain the .exe file.The desired location of this region is specified by the .exe file itself.by default ,base address is 0x00400000 .You can change it by create your app .exe fiel using linker’s /BASE option.

            5.the systems notes that the physical storage baking the reserved region is in the .exe file on disk instread of the system’s paging file.(系統注意到,支持已保留區域的物理存儲區域是在磁盤上的.exe 文件而不是在系統頁面中)

            當。exe 被mapped into the process address space,系統訪問.exe的一個區域,那里列出了.exe含有的dll,然后系統利用 LoadLibrary for each dlls。系統map dll時候,如果dll的 preferred base address 被占據或者不夠大,dllwill try to find another region of address space to reserve for the dll。如果dll 被設定了/Fixed when it build,也就是不支持重定位,那么加載失敗。

            如果加載dll或者exe 失敗,彈出對話框并且釋放申請的地址空間。

            after all .exe  dll mapped into the process address space, system can begin exec the .exe file starup code. the sys takes care of all paging,buffering, and caching. 例如,如果一段代碼訪問了一個并不在主存的地址,那么一個錯誤產生,系統察覺到錯誤并且自動的調入page of code from the files image into a page of RAM。

            the sys maps the page of ram to the proper location in the process address and allows the thread  to continue.

            當為一個app創建第二個實例時,系統只是簡單的打開另外一個memory-mapped view  of file-mapping object that identifies the exec files image and create a new process object and a new thread object.利用內存映射文件,多個實例可以共享代碼和數據。實際上,file 是分為多個section ,多個節均對齊于頁邊界。如果一個instance of the app 修改了全局變量,系統應用了copy-on-write 機制,復制修改的頁面,并更新實例的file-mapping view。當我們調試程序時同樣的事情會發生,debuger modify code,sys use cow again。

            當一個進程被加載,系統檢查其所有映射文件頁,系統將所有通常用cow保護的頁面提交給存儲系統,這些頁面僅僅是被提交,當文件被訪問的時候,系統讀入相應的頁面,如果頁面沒有被修改,那么他們可以被換出,如果已經修改,系統將修改過的頁面轉入已經提交的頁面之一(這點很晦澀啊 system swaps the modified page to one of the perviously committed pages in the paging file ,怎么翻譯呢~~~~ :(   )

            ------------------------------------------------------------------------------------------------------

            在可執行文件或者dll中共享靜態變量

            ------------------------------------------------------------------------------------------------------

            內存映射數據文件

            例子:要把一個文件所有字節倒置

            如果利用file mapping 我們告訴系統使用一個虛擬空間的區域來倒置文件,然后告訴把文件的第一個字節映射到保留空間的第一個字節,然后就像處理內存中的字符串一樣處理文件即可,引文系統會幫助你完成文件緩存以及調頁等工作。

            使用流程:

            1.創建一個file kernel object that identifies the file on disk that you want to use as a memory –mapped file

            2.創建一個file mapping kernel object 告訴系統文件的大小,以及你準備如何訪問文件

            3.告訴系統map all或者部分文件到你的進程地址空間

            當使用結束后要:

            1告訴系統 unmap file-mapping kernel object form your process add

            2cloes filemapping kernel object

            3close file kernel object

            ---------

            具體步驟

            --1. 創建文件內核對象

            CreateFile

            失敗返回 INVALID_HANDLE_VALUE = –1 一般來說windows func 失敗return null這個比較特殊

            createfile dwdesiredAccess 需要設置為 GENERIC_READ 或者 GENERIC_WRITE 

            --2. 創建file-mapping 內核對象

            CreatefileMapping(HANDLE,PSECURITY_ATTRIBUTES,DWORD fdwProtect,DWORD dwMaximumsizeHigh,DWORD dwMaximumSizeLow,PCTSTR pszName);

            第一個參數使用createfile 返回的handle。psa一般使用默認null。當創建一個file mapping object 時候,系統并不會 馬上保留一個地址空間,然后將file映射到這個區域。但很i,當系統map時候,系統必須知道為physical storage分配什么樣的保護屬性,第三個參數指明了這些。

            后面兩個參數指明file的大小,ensure  enouth physical storage is available for the file-mapping object.

            high指明高32位,low指明低32位。如果想創建一個反應現在文件大小的map,均傳0.

            pszName 用于與其它進程共享內存映射文件

            --3.將文件數據map to process address space

            使用這個函數

            PVOID MapViewOfFile(HANDLE hfileMappingObject,dwDesireaccess,dwFileOffsetHigh,dwFileOffsetLow,dwNumberOfbytestomap)

            文件沒必要一次全映射,一次映射一部分,這一部分成為一個view

            首先通過high和low 指定開始映射的字節

            其次是指定映射多大,0則映射到文件尾部。

            --4.unmapping the file data from the process address space

            UnmapviewOfFile(PVOID pvBaseAdddress);

            參數使用mapview的返回值

            如果強制write back to disk 則使用 FlushViewOfFile(PVOID pvAddress,SIZE_T dwNumberOfBytesToFlush)

            第一個地址是想要開始flush的地址

            --5.關閉filemapping object 以及file object

            -----------------------------------------------------------------------------------

            使用filemap 在進程之間共享數據

            例子:

            app開始時候,系統調用createfile to open .exe file onthe disk。sys call creatFileMapping to create filemapping object.然后系統調用  mapviewofffileEX (with sec_image flag to point out it is a pe file),So that the file’s image is mapped to the address of the first byte of exectuable code of this mapped view. System creates the primary thread , puts the address of the first byte of exec code of this mapped view in the thread instruction pointer,and then lets the cpu start exec the code.

            If user 再啟動同一個app,sys 看到file-mapping已經存在了,系統maps a view of file a second time,this time in the context of the newly created process address space.

            像所有內核對象一樣,有三種方法共享他,繼承,命名對象以及賦值handle。

            ···頁文件支持的內存映射文件

            許多應用程序運行是產生數據需要和其他進程共享如果必須在硬盤建立文件才可以共享,那么效率很低。因此微軟提供了由system paging file 支持的 file mapping。不需要createfile ,只需要在調用createFilemapping 的時候傳進一個 INVALID_HANDLE_VALUE 作為hFile 參數即可。 映射文件大小同樣是由high 和low 參數決定的。

            `````稀疏提交的內存映射文件

            --看來需要把虛擬內存那章一起看看了~~~~

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            posted @ 2010-05-26 17:48 rikisand 閱讀(1050) | 評論 (0)編輯 收藏

            2010年5月25日

            A 簡單的模擬 ,不過我寫的很麻煩

            Problem

            On Unix computers, data is stored in directories. There is one root directory, and this might have several directories contained inside of it, each with different names. These directories might have even more directories contained inside of them, and so on.

            A directory is uniquely identified by its name and its parent directory (the directory it is directly contained in). This is usually encoded in a path, which consists of several parts each preceded by a forward slash ('/'). The final part is the name of the directory, and everything else gives the path of its parent directory. For example, consider the path:

            /home/gcj/finals
            
            This refers to the directory with name "finals" in the directory described by "/home/gcj", which in turn refers to the directory with name "gcj" in the directory described by the path "/home". In this path, there is only one part, which means it refers to the directory with the name "home" in the root directory.

            To create a directory, you can use the mkdir command. You specify a path, and thenmkdir will create the directory described by that path, but only if the parent directory already exists. For example, if you wanted to create the "/home/gcj/finals" and "/home/gcj/quals" directories from scratch, you would need four commands:

            mkdir /home
            mkdir /home/gcj
            mkdir /home/gcj/finals
            mkdir /home/gcj/quals
            

            Given the full set of directories already existing on your computer, and a set of new directories you want to create if they do not already exist, how many mkdir commands do you need to use?

            Input

            The first line of the input gives the number of test cases, T. T test cases follow. Each case begins with a line containing two integers N and M, separated by a space.

            The next N lines each give the path of one directory that already exists on your computer. This list will include every directory already on your computer other than the root directory. (The root directory is on every computer, so there is no need to list it explicitly.)

            The next M lines each give the path of one directory that you want to create.

            Each of the paths in the input is formatted as in the problem statement above. Specifically, a path consists of one or more lower-case alpha-numeric strings (i.e., strings containing only the symbols 'a'-'z' and '0'-'9'), each preceded by a single forward slash. These alpha-numeric strings are never empty.

            Output

            For each test case, output one line containing "Case #x: y", where x is the case number (starting from 1) and y is the number of mkdir you need.

            Limits

            1 ≤ T ≤ 100.
            No path will have more than 100 characters in it.
            No path will appear twice in the list of directories already on your computer, or in the list of directories you wish to create. A path may appear once in both lists however. (See example case #2 below).
            If a directory is listed as being on your computer, then its parent directory will also be listed, unless the parent is the root directory.
            The input file will be no longer than 100,000 bytes in total.

            Small dataset

            0 ≤ N ≤ 10.
            1 ≤ M ≤ 10.

            Large dataset

            0 ≤ N ≤ 100.
            1 ≤ M ≤ 100.

            Sample

            Input
            Output

            3
            0 2
            /home/gcj/finals
            /home/gcj/quals
            2 1
            /chicken
            /chicken/egg
            /chicken
            1 3
            /a
            /a/b
            /a/c
            /b/b

            Gluk的解法:

             set <string> S;
                    REP (i, n)
                    {
                        string s;
                        cin >> s;
                        S.insert(s);//已有的路徑用set表示
                    }
            
                    int res =0 ;
            
                    REP (i, m)
                    {
                        string s;
                        cin >> s;
                        vector <string> ss;
                        REP (j, s.size())
                            if(s[j] == '/')
                                s[j] = ' ';//用空格替換’/’,然后使用istringstream分隔各級目錄
                        istringstream iss (s);
                        while (iss >> s) {
                            ss.pb (s);
                        }
            
                        s = "";
                        REP (i, ss.size ())
                        {
                            s = s+"/"+ss[i];
                            if (S.find(s) == S.end())//依次加入各級目錄,/a  /a/b  /a/b/c 增加遞增的所有目錄
                            {
                                res ++;
                                S.insert(s);
                            }
                        }
                    }
            題目中的這一句
            If a directory is listed as being on your computer, then its parent directory will also be listed, unless the parent is the root directory.
            告訴我們如果/a/b被list存在,那么/a也一定被list出來了 ,所以上面代碼可以不去分隔處理已經給出的目錄
            yuhch123的解法
            struct node {
               map <string, node *> sons;//每個節點,用map實現兒子節點
            };
            node *root;//一個根節點
            int T;
            int N, M;
            char tmp[100005];
            int ans = 0;
            void insert(node *cnt, char *tmp) {//在節點cnt處,插入tmp子樹
               int i;
               if (tmp[0] == 0)//為空則返回
                  return;
               assert(tmp[0] == '/');
               string str;
               for (i = 1; tmp[i] != '/' && tmp[i] != 0; i ++)//第一層
                  str += tmp[i];
               if (cnt -> sons.find(str) == cnt -> sons.end()) {//如果沒有這子樹,則創建子樹
                  ans ++;//需要一次mkdir 
                  struct node *tmp2 = new node();
                  cnt -> sons[str] = tmp2;
               }
               insert(cnt -> sons[str], tmp + i);// 遞歸創建子樹
            }
            int main() {
               int i;
               int Case = 1;
               scanf("%d", &T);
               while (T --) {
                  scanf("%d%d", &N, &M);
                  root = new node();
                  for (i = 0; i < N; i ++) {
                 scanf("%s", tmp);
                 insert(root, tmp);
                  }
                  ans = 0;
                  for (i = 0; i < M; i ++) {
                 scanf("%s", tmp);
                 insert(root, tmp);
                  }
                  printf("Case #%d: %d\n", Case ++, ans);
               }
               return 0;
            }
            neal.wu的解法
            vector <string> parse (string s)
            {
                vector <string> v;
                string next = "";
                
                for (int i = 0; i < (int) s.length (); i++)
                {
                    next += s [i];
                    
                    if (i + 1 == (int) s.length () || s [i + 1] == '/')
                    {
                        v.push_back (next);
                        next = "";
                    }
                }
                
                return v;
            }
            
            set <string> direc;
            
            int insert (vector <string> v)
            {
                int count = 0;
                string s = "";
                
                for (int i = 0; i < (int) v.size (); i++)
                {
                    s += v [i];
                    count += direc.insert (s).second ? 1 : 0; //set返回一個pair<iterator,bool> bool指示插入是否成功   
             }
                
                return count;
            }
            
            int main ()
            {
                         freopen("d:\\input.txt","r",stdin);
              
                for (scanf ("%d", &T); TC <= T; TC++)
                {
                    scanf ("%d %d", &N, &M);
                    direc.clear ();
                    int make = 0;
                    char str [1000];
                    
                    for (int i = 0; i < N; i++)
                    {
                        do gets (str); while (strlen (str) == 0);//do gets 過濾回車空字符串
                        insert (parse (str));
                    }
                    
                    for (int i = 0; i < M; i++)
                    {
                        do gets (str); while (strlen (str) == 0);
                        make += insert (parse (str));
                    }
                    
                    printf ("Case #%d: %d\n", TC, make);
                }
                
                //system ("pause");
                return 0;
            }
            ploh的解法:
            int main(void)
            {freopen("d:\\input.txt","r",stdin);
              int T; cin >> T;
              for (int t = 1; t <= T; t++) {
                int ans = 0;
                set <string> have, want;
                int N, M;
                cin >> N >> M;
                for (int i = 0; i < N; i++) {
                  string path; cin >> path;
                  have.insert(path);
                }
                for (int i = 0; i < M; i++) {//用一個set保存所有需要加入的
                  string path; cin >> path;
                  want.insert(path);
                  for (int j = 1; j < path.length(); j++)
                if (path[j] == '/')
                  want.insert(path.substr(0, j));
                }
                for (set <string>::iterator it = want.begin(); it != want.end(); it++)//遍歷所有需要加入的,然后看是否存在
                  if (!have.count(*it))
                ans++;
                printf("Case #%d: %d\n", t, ans);
              }
            }
            
            

            posted @ 2010-05-25 23:29 rikisand 閱讀(367) | 評論 (0)編輯 收藏

            2010年5月1日

            這學期也開始了好久,總結下。

            項目上:基本弄完了碼頭,算是結束了吧,剩下的工作是好好總結下學到的東西,把架構,用到的技術都理順了,方便之后面試回答。

            生活上:這半個月gf生病了,嘉定閔行兩頭跑果然很累,不過還好一直抽空看書。健康很重要~~ 要抓緊鍛煉身體了。本錢一定要掌握好。

            找實習:先后筆試了摩根it和微軟。上周摩根給了電面,全英文的。完全沒有思想準備的我正在在閔行gf那里照顧她,手邊什么書也沒有,項目資料也沒有,我的處女面就這樣“裸著”度過了,到現在還沒有消息,也不知道是不是默劇了。 微軟是上周六筆試的,也沒有消息,sigh~不知道是沒改完卷子還是杯具了~~

            關于實習的筆試和面試要專門寫東西總結一下,方便以后查閱。

            1-4號放假,應該不會有新的安排進來(面試之類的),得安排下事情

            上周把大話設計模式翻了一遍,利用這幾天要仔細的重新過一遍,每一個模式都寫代碼實踐一下。

            然后想看看c++標準文檔,這個會很慢,可以選擇性的跳過一些東西,總共700多頁,看前300頁就可以了,后面是介紹庫的。一天看75頁,恩 ···比較困難·· 盡力吧

            其他的就不安排了,也做不完的。

            另外,之后學的東西,一定要及時用日志記下來,否則時間長了還要從頭學起`````

             

             

             

             

             

             

            posted @ 2010-05-01 14:31 rikisand 閱讀(330) | 評論 (0)編輯 收藏

            2010年4月7日

            良好的編程風格是:new和delete 配套使用,即如果使用new [] 則使用delete []

            事實上,如果我們使用的是自定義類型int char等,或者我們使用的是沒有內存申請的類,我們使用 delete A;并不會發生什么不好的事情。

            這是由delete 的語義決定的。當我們是申請一組對象時候,編譯器會加入內存大小信息和此段內存相關聯。因此當我們delte A 時,編譯器會按照內存大小收回分給我們的內存。顯然,如果是基本類型或者沒有申請內存的情況這樣的行為是良好的。但是如果我們在自建類型中申請了內存~對不起,編譯器是不知道的,這些申請的內存就是內存泄露,隨著程序不斷進行,堆不斷地被侵蝕·····

            這就是delete的第二個作用,他會施加析構函數在我們申請的內存上,如果我們delete A,只會在第一個上施加,而如果delete [] A;他會對數組中每一個元素進行析構~~

            so····

            試驗很容易做,寫兩個類,一個申請內存,一個普通的類,然后循環申請大量數組,但是用 delete A 形式,然后看看內存占用就行了

            posted @ 2010-04-07 10:42 rikisand 閱讀(518) | 評論 (1)編輯 收藏

            2010年4月4日

            數據對齊:是指數據所在的內存地址必須是該數據長度的整數倍。處理器可以直接訪問對齊的數據,而訪問未對齊的數據會在內部進行一系列的調整,雖然可以正常處理,但是會降低運行速度。例如一個處理器總線寬度為64位,那么地址必須為8的倍數,也就是一次取出8個字節。如果我們可以保證所有double地址都為8的倍數,那么可以用一個存儲器操作來讀或者寫double了,否則我們可能需要執行兩次存儲器訪問,因為對象可能位于兩個8字節存儲器塊中

            對于結構體也是一樣的例如 struct s2{int i;int j;char c;}; 如果我們把這個結構打包成9個字節,只要保證起始地址滿足4的對齊要求我們就可以保證i j的對齊,不過如果我們聲明了一個數組,那么元素地址成為 xd,xd+9,xd+18,xd+27 不滿足對齊了。因此我們呢要分配12個字節給這個結構體

            更進一步 :

            未對齊的數據會以不同方式給cpu帶來麻煩~

            1.如果一個變量被劃分到兩個緩存行,那么我們需要訪問兩個緩存行才可以。

            2.一些simd指令總是要求對齊的指令,對于未對齊的指令,數據對齊也會影響這些指令的使用。

            posted @ 2010-04-04 16:00 rikisand 閱讀(443) | 評論 (3)編輯 收藏

            2010年3月26日

            Drainage Ditches
            Hal Burch

            Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover patch. This means that the clover is covered by water for awhile and takes quite a long time to regrow. Thus, Farmer John has built a set of drainage ditches so that Bessie's clover patch is never covered in water. Instead, the water is drained to a nearby stream. Being an ace engineer, Farmer John has also installed regulators at the beginning of each ditch, so he can control at what rate water flows into that ditch.

            Farmer John knows not only how many gallons of water each ditch can transport per minute but also the exact layout of the ditches, which feed out of the pond and into each other and stream in a potentially complex network. Note however, that there can be more than one ditch between two intersections.

            Given all this information, determine the maximum rate at which water can be transported out of the pond and into the stream. For any given ditch, water flows in only one direction, but there might be a way that water can flow in a circle.

            PROGRAM NAME: ditch
            INPUT FORMAT

            Line 1:
            Two space-separated integers, N (0 <= N <= 200) and M (2 <= M <= 200). N is the number of ditches that Farmer John has dug. M is the number of intersections points for those ditches. Intersection 1 is the pond. Intersection point M is the stream.

            Line 2..N+1:
            Each of N lines contains three integers, Si, Ei, and Ci. Si and Ei (1 <= Si, Ei <= M) designate the intersections between which this ditch flows. Water will flow through this ditch from Si to Ei. Ci (0 <= Ci <= 10,000,000) is the maximum rate at which water will flow through the ditch.

            SAMPLE INPUT (file ditch.in)
            5 4
            1 2 40
            1 4 20
            2 4 20
            2 3 30
            3 4 10
            
            OUTPUT FORMAT

            One line with a single integer, the maximum rate at which water may emptied from the pond.

            SAMPLE OUTPUT (file ditch.out)
            50
            最基本的網絡流
               1:  #include<iostream>
               2:  #include<fstream>
               3:  #include<string>
               4:  #include<vector>
               5:  #include<map>
               6:  #include<algorithm>
               7:  #include<sstream>
               8:  #include <cstring>
               9:  #include <queue>
              10:  using namespace std;
              11:  const int MAXN = 220;
              12:  const int infi = 0x7FFFFFFF;
              13:   int capacity[MAXN][MAXN], prenode[MAXN], flow[MAXN];
              14:   queue<int> mq; 
              15:   
              16:  int start, end, N;
              17:  void init(){
              18:      freopen("ditch.in","r",stdin);
              19:      //freopen("e:\\usaco\\ditch.in","r",stdin);
              20:      start = 1;  
              21:      scanf("%d %d",&N,&end); int c, s, t;
              22:      memset(capacity,0,sizeof(capacity));
              23:      for(int i=0;i<N;i++)
              24:      {
              25:          scanf("%d %d %d",&c,&s,&t);
              26:          capacity[c][s] += t; //兩個節點間不只有一條路
              27:      } 
              28:  }
              29:  int bfs(){//尋找增廣路徑
              30:      while(!mq.empty()) mq.pop(); 
              31:      mq.push(start);  //源節點入隊
              32:      //memset(flow,0,sizeof(flow));
              33:      memset(prenode,-1,sizeof(prenode)); //重置前向節點
              34:      prenode[start] = 0; flow[start]=infi; //源節點流量無限大
              35:      while(!mq.empty()){
              36:          int cur = mq.front(); 
              37:          mq.pop();
              38:          if(cur == end) break; //到達匯點結束路徑 
              39:          for(int i=1;i<=end;i++){ 
              40:              if(prenode[i]==-1 && capacity[cur][i]) //訪問當前節點所有未訪問的相鄰節點,更新flow
              41:              {
              42:                  prenode[i] = cur;
              43:                  flow[i] = (flow[cur]<capacity[cur][i]?flow[cur]:capacity[cur][i]);
              44:                  mq.push(i);
              45:              }
              46:          }
              47:      }
              48:      if(prenode[end]==-1)  //如果未找到增廣路徑返回-1
              49:          return -1;
              50:      return flow[end];
              51:  }
              52:  int Edmonds_Karp(){
              53:      int total = 0, pathcapacity;//pathcapacity 路徑增加量
              54:      while((pathcapacity = bfs()) != -1){//可以找到增廣路徑時候進行循環
              55:          int cur = end;    //從匯點開始增加逆向節點
              56:          while( cur != start ){
              57:              int t = prenode[cur] ;
              58:              capacity[t][cur] -= pathcapacity;
              59:              capacity[cur][t] += pathcapacity;
              60:              cur = t;
              61:          }
              62:          total += pathcapacity;//max_flow
              63:      }
              64:      return total;
              65:  }
              66:  void output(){
              67:      freopen("ditch.out","w",stdout);
              68:      //freopen("c:\\usaco\\ditch.out","w",stdout);
              69:      cout<<Edmonds_Karp()<<endl;
              70:  } 
              71:     int main()
              72:  {
              73:      init();  
              74:      output();
              75:      return 0;
              76:  }

            標程:使用貪心法,尋找一條增廣路徑的時候不斷尋找cap最大的,未被訪問的節點mloc;然后更新跟mloc相鄰的節點flow以

            及prenode信息.最后當運行到end時候,更新路徑節點capacity,同時增加max_flow.重復上述過程直到找不到增廣路徑

               1:  #include <stdio.h>
               2:  #include <string.h>
               3:   
               4:  #define MAXI 200
               5:   
               6:  /* total drain amount between intersection points */
               7:  int drain[MAXI][MAXI];
               8:  int nint; /* number of intersection points */
               9:   
              10:  int cap[MAXI]; /* amount of flow that can get to each node */
              11:  int vis[MAXI]; /* has this node been visited by Dijkstra's yet? */
              12:  int src[MAXI]; /* the previous node on the path from the source to here */
              13:   
              14:  int augment(void)
              15:   { /* run a Dijkstra's varient to find maximum augmenting path */
              16:    int lv;
              17:    int mloc, max;
              18:    int t;
              19:   
              20:    memset(cap, 0, sizeof(cap));
              21:    memset(vis, 0, sizeof(vis));
              22:   
              23:    cap[0] = 2000000000;
              24:    while (1)
              25:     {
              26:      /* find maximum unvisited node */
              27:      max = 0;
              28:      mloc = -1;
              29:      for (lv = 0; lv < nint; lv++)
              30:        if (!vis[lv] && cap[lv] > max)
              31:         {
              32:          max = cap[lv];
              33:      mloc = lv;
              34:         }
              35:      if (mloc == -1) return 0;
              36:      if (mloc == nint-1) break; /* max is the goal, we're done */
              37:   
              38:      vis[mloc] = -1; /* mark as visited */
              39:   
              40:      /* update neighbors, if going through this node improves the
              41:         capacity */
              42:      for (lv = 0; lv < nint; lv++)
              43:        if (drain[mloc][lv] > cap[lv] && max > cap[lv])
              44:         {
              45:          cap[lv] = drain[mloc][lv];
              46:      if (cap[lv] > max) cap[lv] = max;
              47:      src[lv] = mloc;
              48:         }
              49:     }
              50:    max = cap[nint-1];
              51:   
              52:    /* augment path, starting at end */
              53:    for (lv = nint-1; lv > 0; lv = src[lv])
              54:     {
              55:      t = src[lv];
              56:      drain[t][lv] -= max;
              57:      drain[lv][t] += max;
              58:     }
              59:    return max;
              60:   }
              61:   
              62:  int main(int argc, char **argv)
              63:   {
              64:    FILE *fout, *fin;
              65:    int lv;
              66:    int num;
              67:    int p1, p2, c;
              68:   
              69:    if ((fin = fopen("ditch.in", "r")) == NULL)
              70:     {
              71:      perror ("fopen fin");
              72:      exit(1);
              73:     }
              74:    if ((fout = fopen("ditch.out", "w")) == NULL)
              75:     {
              76:      perror ("fopen fout");
              77:      exit(1);
              78:     }
              79:   
              80:    fscanf (fin, "%d %d", &num, &nint);
              81:    while (num--)
              82:     {
              83:      fscanf (fin, "%d %d %d", &p1, &p2, &c);
              84:      p1--;
              85:      p2--;
              86:      drain[p1][p2] += c; /* note += handles two ditches between same points */
              87:     }
              88:   
              89:    /* max flow algorithm: augment while you can */
              90:    c = 0;
              91:    while ((p1 = augment()))
              92:      c += p1;
              93:    fprintf (fout, "%d\n", c);
              94:    return 0;
              95:   }

             

             

             

             

             

             

             

             

            posted @ 2010-03-26 13:04 rikisand 閱讀(421) | 評論 (0)編輯 收藏

            2010年3月25日

            最后一章~~拖了幾天,得趕緊記下了~~

            名字: On the cusp of the object model

            7.1 Template

            Template用的很少,這節中的有些東西比較晦澀,挑一些能理解的記下吧。剩下的等用的多了再回頭來看。

            Template的具現行為 template instantiation

            Template <calss T>class Point{

              public: enum Status{unallocated,normalized};Point(T x=0.0,T y=0.0);

            ~Point() ; void * operator new(size_t);private:static Point<T> *freelist

            static int chunksize; T _x,_y;

            };

            編譯器看到這樣的聲明,會有什么動作??

            沒有。static data 不可用 enum 不可用 ,enum雖然類型固定,但他只能和template point class 的某個實體來存取或操作。我們可以 point<float>::Status s;

            但是不能 point::status s; 因此我們可能想把enum抽離到一個非模板類以避免多重拷貝。同樣道理,freelist 和chunksize對于程序而言也不可用。必須明確指定point<float>::freelist.也就是說靜態數據成員是和特定類型的類綁定的而不是泛型的模板類。但是如果定義一個指針不一定會具現出相應的類,因為一個指針,并不一定指向一個class object。編譯器不需要知道該class 相應的任何member 或者數據或者

            內存布局,所以沒有必要具現。但是如果是定義并初始化一個引用就真的會具現出一個實體,因為不存在空引用。

            成員函數并不應該被實體化,只有當他被調用的時候才需要被具現出來。

            template<class T>

            class mumble{

            public: Muble(T t=1024):tt(t){ if(tt!=t)throw ex; }

            private: T tt;

            };

            上面的模板中出現錯誤有,t=1024 不一定成功,!= 不一定定義

            這兩個錯誤只有到具現操作結合具體類型才能確定出來

            編譯器面對一個template聲明,在他被一組實際參數具現之前,只能實行以有限地錯誤檢查,只有特定實體定義之后,才會發現一些與語法無關的但是十分明顯的錯誤,這是技術上的一大問題。

            Template 的名稱決議方式

            .h文件定義

            void out(int x){
                cout<<"out_by_class_define_scrope"<<endl;
            }
            template <class type>
            class A{
            public:
                void test1(){
                    out(m1);
                }
                void test2(){
                    out(m2);
                }
            private:
                int m1;
                type m2;
            };

            .cpp文件定義

            void out(double x){
                cout<<"out_by_class_instantiation_scrope"<<endl;
            }

            int main()
            {    
                A<double> a;
                a.test1();
                a.test2();

            }

            按照書中的說法,如果一個函數,參數類型和type無關的話,應該取他的scope of template declaration中定義的函數,而反之取在他的instantiation中的那個。事實上在測試中發現

            MSVC 中 ,函數定義的決議是依照類型的,如果有合適的函數比如type是double,此時如果定義處或者具現處有double型的函數定義,那么函數就會決議為那一個定義的~~~

            MEMber function的具現行為:

            編譯器如何確保只有一個vtable產生? 一種方法是 每一個virtual func地址都放在vtable中,如果取得函數地址,則表示virtual func 的定義必然出現在程序的某個地點,否則程序無法連接成功。此外該函數只能有一個實體,否則也是連接不成功。那么,就把vtable放在定義了該class的第一個non-inline,nonpure virtual function的文件中吧。。(not so clear)

            在實現層面上,template 似乎拒絕全面自動化,以手動方式在個別的object module中完成預先的具現工作是一種有效率的方法。

            7.2異常處理

            為了維持執行速度,編譯器可以在編譯時期建立起用于支持的數據結構

            為了維持程序大小,編譯器可以在執行期建立數據結構

            c++ eH 主要由三個部分構成:

            throw語句

            catch語句

            try語句,這些語句可能觸發catch子句起作用

            一個exception 被拋出時候,控制權會從函數調用釋放出來,并尋找一個吻合的catch,如果沒有那么默認的處理例程terminate()會被調用,控制權放棄后,堆棧中的每一個函數調用也被推離。每一個函數被推離堆棧的時候,函數的local class object 的dtor也會被調用。

            在程序不同段里,由于聲明不同的變量,一個區域可能因為在區域內發生exception的處理方式不同分成多個區段。

            在程序員層面,eh也改變了函數在資源上的管理。例如下面函數中更含有對一塊共享內存的locking和unlocking操作 :

            void mumble(void * arena){

            Point *p= new point ;

            smlock(arena) ;

            //…..如果此時一個exception發生,問題就產生了

            sumunlock(arena);

            delete p;

            }

            從語義上講,我們在函數退出堆棧之前,需要unlock共享內存并delete p,我們需要這樣做:

            try{smlock(arena)} catch(…){

                smunlock(arena); delete p; throw;

            }

            new不需要放在try段里,因為,如果new發生了exception,heap中的內存并沒有分配,point的ctor沒有調用,如果在ctor中exception,那么piont的任何構造好的合成物也會自動解構掉,heap也會自動釋放掉。

            處理這種資源管理問題,建議: 把資源需求封裝在一個class object 體內,并由dtor釋放資源.

            auto_ptr<point> ph (new point); smlock sm(arena);//如果此時exception 沒有問題

            // 不需要明確的unlock 和delete

            // local dtor 會在這里被調用 sm.SMlock::~smlock(); ph.auto_ptr<point>::~auto_ptr<point>

            Exception handling 的支持:

            1.檢驗發生throw操作的函數

            2.決定throw是否發生在try區段里

            3.如果是編譯器必須把exception type 拿來和catch比較

            4.吻合的話控制權交給catch

            5.如果throw不發生在try段或者沒有吻合的,系統會摧毀所有active local object b從堆棧把當前函數unwind掉 ,跳出到程序堆棧的下一個函數去,然后重復上述步驟

            當一個實際對象在程序執行時被拋出,exception object會被產生出來并通常放在相同形式的exception 數據堆棧中。

            catch(expoint p){

               //do sth

               throw;

            }

            以及一個exception object 類型為 exVertex 派生自 expoint ,這兩種類型都吻合,catch會怎么做

            以exception object作為初值,像函數參數一樣會有一個local copy,如果p是一個object而不是一個reference ,內容被拷貝的時候,這個object的非expoint部分會被切掉,如果有vptr 會被設定為expoint的vptr,如果被再次丟出呢?丟出p需要再產生另外一個exception 臨時對象,丟出原來的異常 ,之前的修改統統作廢。但是如果 catch(expoint& p);怎么樣呢。 任何對這個object的改變都會繁殖到之后的catch語句總。

            c++ 編譯器為了支持EH付出的代價最大,某種程度是因為執行期的天性以及對底層硬件的依賴。

            7.3 RTTI

            RTTI是Except handling的一個附屬產品,因為我們需要提供某種查詢exception objects的方法,用來得到exception的實際類型。

            在向下轉型問題上,如果要保證其安全性,那么必須在執行期對指針有所查詢,看看它到底指向什么類型的對象。那么我們需要額外的空間存儲類型信息,通常是一個指針,指某個類型信息節點。

            需要額外的時間以決定執行期的類型。

            沖突發生在:

            1.程序員大量的使用了多臺,并需要大量的downcast操作

            2.程序員使用內建的數據類型和非多態,他不需要額外負擔帶來的便利

            那么如何了解程序員的需要呢?? 策略是一個具有多態性性質的class,也就是具有內涵繼承或者聲明 virtual func的類需要rtti支持。

            這樣所有多態類維護一個vptr。額外負擔降低到:每一個class object多花費一個指針,指針只被設定一次,而且是編譯器靜態設定。

            down_cast

            if(pfct pf = dynamic_cast< pfct >(pt))….

            ((type_info*)(pt->vptr[0]))->type_descripter;

            Reference --------Pointer

            dynamic_cast執行在ptr上 失敗返回0,如果實行在ref上。由于ref不能被賦予null,也就是沒有空引用。如果我們把一個ref設為0會引發臨時對象產生,然后用0初始化它,使ref成為這個臨時對象的別名。因此此時失敗了會產生一個bad_cast exception。

            typeid的參數可以使引用,對象或者是類型

            事實上,type_info 也適用內建類型,這對于eh機制有必要

            例如 int ex_errno; throw ex_errno;

            其中int類型 int *ptr; if(typeid(ptr) == typeid(int*));

            ----------------------全書筆記到此結束 --------------------------------s

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            posted @ 2010-03-25 21:09 rikisand 閱讀(423) | 評論 (0)編輯 收藏

            2010年3月22日

             

            第一組

            1.燒一根不均勻的繩,從頭燒到尾總共需要1個小時。現在有若干條材質相同的繩子,問如何用燒繩的方法來計時一個小時十五分鐘呢?
            2.你有一桶果凍,其中有黃色、綠色、紅色三種,閉上眼睛抓取同種顏色的兩個。抓取多少個就可以確定你肯定有兩個同一顏色的果凍?
            3.如果你有無窮多的水,一個3公升的提捅,一個5公升的提捅,兩只提捅形狀上下都不均勻,問你如何才能準確稱出4公升的水?
            4.一個岔路口分別通向誠實國和說謊國。來了兩個人,已知一個是誠實國的,另一個是說謊國的。誠實國永遠說實話,說謊國永遠說謊話。現在你要去說謊國,但不知道應該走哪條路,需要問這兩個人。請問應該怎么問?
            5.12個球一個天平,現知道只有一個和其它的重量不同,問怎樣稱才能用三次就找到那個球。13個呢?(注意此題并未說明那個球的重量是輕是重,所以需要仔細考慮)
            6.在9個點上畫10條直線,要求每條直線上至少有三個點?
            7.在一天的24小時之中,時鐘的時針、分針和秒針完全重合在一起的時候有幾次?都分別是什么時間?你怎樣算出來的?
            8.怎么樣種植4棵樹木,使其中任意兩棵樹的距離相等?

            第二組

            1.為什么下水道的蓋子是圓的?
            2.中國有多少輛汽車?
            3.將汽車鑰匙插入車門,向哪個方向旋轉就可以打開車鎖?
            4.如果你要去掉中國的34個省(含自治區、直轄市和港澳特區及臺灣省)中的任何一個,你會去掉哪一個,為什么?
            5.多少個加油站才能滿足中國的所有汽車?
            6.想象你站在鏡子前,請問,為什么鏡子中的影象可以顛倒左右,卻不能顛倒上下?
            7.為什么在任何旅館里,你打開熱水,熱水都會瞬間傾瀉而出?
            8.你怎樣將Excel的用法解釋給你的奶奶聽?
            9.你怎樣重新改進和設計一個ATM銀行自動取款機?
            10.如果你不得不重新學習一種新的計算機語言,你打算怎樣著手來開始?
            11.如果你的生涯規劃中打算在5年內受到獎勵,那獲取該項獎勵的動機是什么?觀眾是誰?
            12.如果微軟告訴你,我們打算投資五百萬美元來啟動你的投資計劃,你將開始什么樣商業計劃?為什么?
            13.如果你能夠將全世界的電腦廠商集合在一個辦公室里,然后告訴他們將被強迫做一件事,那件事將是什么?

            第三組

            1.你讓工人為你工作7天,回報是一根金條,這個金條平分成相連的7段,你必須在每天結束的時候給他們一段金條。如果只允許你兩次把金條弄斷,你如何給你的工人付費?
            2.有一輛火車以每小時15公里的速度離開北京直奔廣州,同時另一輛火車每小時20公里的速度從廣州開往北京。如果有一只鳥,以30公里每小時的速度和兩輛火車同時啟動,從北京出發,碰到另一輛車后就向相反的方向返回去飛,就這樣依次在兩輛火車之間來回地飛,直到兩輛火車相遇。請問,這只鳥共飛行了多長的距離?
            3.你有四個裝藥丸的罐子,每個藥丸都有一定的重量,被污染的藥丸是沒被污染的藥丸的重量+1。只稱量一次,如何判斷哪個罐子的藥被污染了?
            4.門外三個開關分別對應室內三盞燈,線路良好,在門外控制開關時候不能看到室內燈的情況,現在只允許進門一次,確定開關和燈的對應關系?
            5.人民幣為什么只有1、2、5、10的面值?
            6.你有兩個罐子以及50個紅色彈球和50個藍色彈球,隨機選出一個罐子, 隨機選出一個彈球放入罐子,怎么給出紅色彈球最大的選中機會?在你的計劃里,得到紅球的幾率是多少?
            7.給你兩顆6面色子,可以在它們各個面上刻上0-9任意一個數字,要求能夠用它們拼出任意一年中的日期數值

            第四組

            第一題 . 五個海盜搶到了100顆寶石,每一顆都一樣大小和價值連城。他們決定這么分:
            抽簽決定自己的號碼(1、2、3、4、5)
            首先,由1號提出分配方案,然后大家表決,當且僅當超過半數的人同意時,按照他的方案
            進行分配,否則將被扔進大海喂鯊魚
            如果1號死后,再由2號提出分配方案,然后剩下的4人進行表決,當且僅當超過半數的人同
            意時,按照他的方案進行分配,否則將被扔入大海喂鯊魚
            依此類推
            條件:每個海盜都是很聰明的人,都能很理智地做出判斷,從而做出選擇。
            問題:第一個海盜提出怎樣的分配方案才能使自己的收益最大化?

            第二題 . 一道關于飛機加油的問題,已知:
            每個飛機只有一個油箱,
            飛機之間可以相互加油(注意是相互,沒有加油機)
            一箱油可供一架飛機繞地球飛半圈,
            問題:
            為使至少一架飛機繞地球一圈回到起飛時的飛機場,至少需要出動幾架飛機?(所有飛機從同一機場起飛,而且必須安全返回機場,不允許中途降落,中間沒有飛機場)

            第三題. 汽車加油問題
            一輛載油500升的汽車從A開往1000公里外的B,已知汽車每公里耗油量為1升,A處有無窮多的油,其他任何地點都沒有油,但該車可以在任何地點存放油以備中轉,問從A到B最少需要多少油

            第四題. 擲杯問題
            一種杯子,若在第N層被摔破,則在任何比N高的樓層均會破,若在第M層不破,則在任何比M低的樓層均會破,給你兩個這樣的杯子,讓你在100層高的樓層中測試,要求用最少的測試次數找出恰巧會使杯子破碎的樓層。

            第五題. 推理游戲
            教授選出兩個從2到9的數,把它們的和告訴學生甲,把它們的積告訴學生乙,讓他們輪流猜這兩個數
            甲說:“我猜不出”
            乙說:“我猜不出”
            甲說:“我猜到了”
            乙說:“我也猜到了”
            問這兩個數是多少

            第六題. 病狗問題
            一個住宅區內有100戶人家,每戶人家養一條狗,每天傍晚大家都在同一個地方遛狗。已知這些狗中有一部分病狗,由于某種原因,狗的主人無法判斷自己的狗是否是病狗,卻能夠分辨其他的狗是否有病,現在,上級傳來通知,要求住戶處決這些病狗,并且不允許指認他人的狗是病狗(就是只能判斷自己的),過了7天之后,所有的病狗都被處決了,問,一共有幾只病狗?為什么?

            第七題. U2合唱團在17分鐘內得趕到演唱會場,途中必需跨過一座橋,四個人從橋的同一端出發,你得幫助他們到達另一端,天色很暗,而他們只有一只手電筒。一次同時最多可以有兩人一起過橋,而過橋的時候必須持有手電筒,所以就得有人把手電筒帶來帶去,來回橋兩端。手電筒是不能用丟的方式來傳遞的。四個人的步行速度各不同,若兩人同行則以較慢者的速度為準。BONO需花1分鐘過橋,EDGE需花2分鐘過橋,ADAM需花5分鐘過橋,LARRY需花10分鐘過橋,他們要如何在17分鐘內過橋呢?

            第八題. 監獄里有100個房間,每個房間內有一囚犯。一天,監獄長說,你們獄房外有一電燈,你們在放風時可以控制這個電燈(熄或亮)。每天只能有一個人出來放風,并且防風是隨機的。如果在有限時間內,你們中的某人能對我說:“我敢保證,現在每個人都已經至少放過一次風了。”我就放了你們!問囚犯們要采取什么策略才能被監獄長放掉?如果采用了這種策略,大致多久他們可以被釋放?

            第五組

            1.某手機廠家由于設計失誤,有可能造成電池壽命比原來設計的壽命短一半(不是沖放電時
            間),解決方案就是免費更換電池或給50元購買該廠家新手機的折換券。請給所有已購買的
            用戶寫信告訴解決方案。
            2.一高層領導在參觀某博物館時,向博物館館員小王要了一塊明代的城磚作為紀念,按國家
            規定,任何人不得將博物館收藏品變為私有。博物館館長需要如何寫信給這位領導,將城磚
            取回。
            3.營業員小姐由于工作失誤,將2萬元的筆記本電腦以1.2萬元錯賣給李先生,王小姐的經理
            怎么寫信給李先生試圖將錢要回來?
            4.給你一款新研制的手機,如果你是測試組的組長,你會如何測試?
            5.如何為函數int atoi(const char * pstr)編寫測試向量?

            第六組

            1.鏈表和數組的區別在哪里?
            2.編寫實現鏈表排序的一種算法。說明為什么你會選擇用這樣的方法?
            3.編寫實現數組排序的一種算法。說明為什么你會選擇用這樣的方法?
            4.請編寫能直接實現char * strcpy(char * pstrDest,const char * pstrSource)函數功能的代碼。
            5.編寫反轉字符串的程序,要求優化速度、優化空間。
            6.在鏈表里如何發現循環鏈接?
            7.給出洗牌的一個算法,并將洗好的牌存儲在一個整形數組里。
            8.寫一個函數,檢查字符是否是整數,如果是,返回其整數值。(或者:怎樣只用4行代碼
            9.給出一個函數來輸出一個字符串的所有排列。
            10.請編寫實現void * malloc(int)內存分配函數功能一樣的代碼。
            11.給出一個函數來復制兩個字符串A和B。字符串A的后幾個字節和字符串B的前幾個字節重疊。
            12.怎樣編寫一個程序,把一個有序整數數組放到二叉樹中?
            13.怎樣從頂部開始逐層打印二叉樹結點數據?請編程。
            14.怎樣把一個鏈表掉個順序(也就是反序,注意鏈表的邊界條件并考慮空鏈表)? --
            15.請編寫能直接實現int atoi(const char * pstr)函數功能的代碼。

            ---------------------------------------------------------------------------------------

            第一組題答案:

            1)三根繩,第一根點燃兩端,第二根點燃一端,第三根不點
            第一根繩燒完(30分鐘)后,點燃第二根繩的另一端,第二根繩燒完(45分鐘)后,點燃第三根繩子兩端,第三根繩燒完(1小時15分)后,計時完成
            2)根據抽屜原理,4個
            3)3升裝滿;3升-〉5升(全注入);3升裝滿;3升-〉5升(剩1升);5升倒掉;3升-〉5升(注入1升);3升裝滿;3升-〉5升;完成(另:可用回溯法編程求解)
            4)問其中一人:另外一個人會說哪一條路是通往誠實國的?回答者所指的那條路必然是通往說謊國的。
            5)12個球:
            第一次:4,4 如果平了:
              那么剩下的球中取3放左邊,取3個好球放右邊,稱:
                如果左邊重,那么取兩個球稱一下,哪個重哪個是次品,平的話第三個重,是次品,輕的話同理
                如果平了,那么剩下一個次品,還可根據需要稱出次品比正品輕或者重
            如果不平:
              那么不妨設左邊重右邊輕,為了便于說明,將左邊4顆稱為重球,右邊4顆稱為輕球,剩下4顆稱為好球
              取重球2顆,輕球2顆放在左側,右側放3顆好球和一顆輕球
              如果左邊重
                  稱那兩顆重球,重的一個次品,平的話右邊輕球次品
              如果右邊重
                  稱左邊兩顆輕球,輕的一個次品
              如果平
                  稱剩下兩顆重球,重的一個次品,平的話剩下那顆輕球次品
            13個球:
            第一次:4,4,如果平了
              剩5顆球用上面的方法仍舊能找出次品,只是不能知道次品是重是輕
            如果不平,同上
            6)
            o   o   o
              o o o
            o   o   o
            7)
            23次,因為分針要轉24圈,時針才能轉1圈,而分針和時針重合兩次之間的間隔顯然>1小時,它們有23次重合機會,每次重合中秒針有一次重合機會,所以是23次
            重合時間可以對照手表求出,也可列方程求出
            8)
            在地球表面種樹,做一個地球內接的正四面體,內接點即為所求

            第二組 無標準答案

            第三組

            1. 分成1,2,4三段,第一天給1,第二天給2取回1,第3天給1,第4天給4取回1、2,第5天給1,第6天給2取回1,第七天給1
            2. 求出火車相遇時間,鳥速乘以時間就是鳥飛行的距離
            3. 四個罐子中分別取1,2,3,4顆藥丸,稱出比正常重多少,即可判斷出那個罐子的藥被污染
            4. 三個開關分別:關,開,開10分鐘,然后進屋,暗且涼的為開關1控制的燈,亮的為開關2控制的燈,暗且熱的為開關3控制的燈
            5. 因為可以用1,2,5,10組合成任何需要的貨幣值,日常習慣為10進制
            6. 題意不理解...*_*
            7. 012345 0126(9)78

            第四組 都是很難的題目

            第一題:97 0 1 2 0 或者 97 0 1 0 2 (提示:可用逆推法求出)

            第二題:3架飛機5架次,飛法:
            ABC 3架同時起飛,1/8處,C給AB加滿油,C返航,1/4處,B給A加滿油,B返航,A到達1/2處,C從機場往另一方向起飛,3/4處,C同已經空油箱的A平分剩余油量,同時B從機場起飛,AC到7/8處同B平分剩余油量,剛好3架飛機同時返航。所以是3架飛機5架次。

            第三題:需要建立數學模型
            (提示,嚴格證明該模型最優比較麻煩,但確實可證,大膽猜想是解題關鍵)
            題目可歸結為求數列 an=500/(2n+1) n=0,1,2,3......的和Sn什么時候大于等于1000,解得n>6
            當n=6時,S6=977.57
            所以第一個中轉點離起始位置距離為1000-977.57=22.43公里
            所以第一次中轉之前共耗油 22.43*(2*7+1)=336.50升
            此后每次中轉耗油500升
            所以總耗油量為7*500+336.50=3836.50升

            第四題:需要建立數學模型
            題目可歸結為求自然數列的和S什么時候大于等于100,解得n>13
            第一個杯子可能的投擲樓層分別為:14,27,39,50,60,69,77,84,90,95,99,100

            第五題:3和4(可嚴格證明)
            設兩個數為n1,n2,n1>=n2,甲聽到的數為n=n1+n2,乙聽到的數為m=n1*n2
            證明n1=3,n2=4是唯一解
            證明:要證以上命題為真,不妨先證n=7
            1)必要性:
               i) n>5 是顯然的,因為n<4不可能,n=4或者n=5甲都不可能回答不知道
               ii) n>6 因為如果n=6的話,那么甲雖然不知道(不確定2+4還是3+3)但是無論是2,4還是3,3乙都不可能說不知道(m=8或者m=9的話乙說不知道是沒有道理的)
              iii) n<8 因為如果n>=8的話,就可以將n分解成 n=4+x 和 n=6+(x-2),那么m可以是4x也可以是6(x-2)而4x=6(x-2)的必要條件是x=6即n=10,那樣n又可以分解成8+2,所以總之當n>=8時,n至少可以分解成兩種不同的合數之和,這樣乙說不知道的時候,甲就沒有理由馬上說知道。
               以上證明了必要性
            2)充分性
                當n=7時,n可以分解成2+5或3+4
                顯然2+5不符合題意,舍去,容易判斷出3+4符合題意,m=12,證畢
            于是得到n=7 m=12 n1=3 n2=4是唯一解。

            第六題:7只(數學歸納法證明)
            1)若只有1只病狗,因為病狗主人看不到有其他病狗,必然會知道自己的狗是病狗(前提是一定存在病狗),所以他會在第一天把病狗處決。
            2)設有k只病狗的話,會在第k天被處決,那么,如果有k+1只,病狗的主人只會看到k只病狗,而第k天沒有人處決病狗,病狗主人就會在第k+1天知道自己的狗是病狗,于是病狗在第k+1天被處決
            3)由1)2)得,若有n只病狗,必然在第n天被處決

            第七題:(提示:可用圖論方法解決)
            BONO&EDGE過(2分),BONO將手電帶回(1分),ADAM&LARRY過(10分),EDGE將手電帶回(2分),BONO&EDGE過(2分) 2+1+10+2+2=17分鐘

            第八題:
            約定好一個人作為報告人(可以是第一個放風的人)
            規則如下:
            1、報告人放風的時候開燈并數開燈次數
            2、其他人第一次遇到開著燈放風時,將燈關閉
            3、當報告人第100次開燈的時候,去向監獄長報告,要求監獄長放人......
            按照概率大約30年后(10000天)他們可以被釋放

            第五組無標準答案

            第六組部分題參考答案:
            4.
            char * strcpy(char * pstrDest,const char * pstrSource)
            {
            assert((pstrDest!=NULL)&&(pstrSource!=NULL));

            char * pstr=pstrDest;
            while((*(pstrDest++)=*(pstrSource++))!='\0');
                    return pstr;
            }
            5.
            char * strrev(char * pstr)
            {
            assert(pstr!=NULL);
            char * p=pstr;
            char * pret=pstr;
            while(*(p++)!='\0');
            p--;
            char tmp;
            while(p>pstr)
            {
              tmp=*p;
              *(p--)=*(pstr);
              *(pstr++)=tmp; 
            }
            return pret;

            posted @ 2010-03-22 23:21 rikisand 閱讀(212) | 評論 (0)編輯 收藏

            2010年3月19日

            執行期語義學 RunTime Semantics

            if( yy == xx.getValue() ) …

            X xx; Y yy;

            class Y{

            public:

            Y();  ~Y();  bool operator == (constY& )const;

            };

            class X{

            public:

            X(); ~X(); operator Y()const; //重載轉換類型操作符 必須成員不能有參數不能有返回值詳細在 http://www.shnenglu.com/zqsand/archive/2010/03/15/109748.html里面有介紹

            X getValue();

            };

            看看上面的表達式怎么執行的~~~

            首先等號運算符的參數確定  if(yy.operator==(xx.getValue()));

            Y的== 需要一個Y型的參數,但是getvalue得到的是一個X型的,如果沒有X到Y的轉型方法,這個式子是錯的~ 而恰好X有個類型轉換符~

            if(yy.operator == (xx.getValue().operator Y()))

            增加的代碼都是編譯器默默為我們加上的~~~

            注意在這個過程中,我們需要一個臨時的Xobject 儲存 getValue返回值 X temp1 = xx.getValue()

            一個class Y object 儲存 operator Y()返回值 Y temp2 = temp1.operator Y();

            一個 int 放置 等號返回值  int tmp3 = yy.operator == (temp2);

            最后析構函數會實施在每一個臨時class object上.

            所以,我們的代碼變成:

            {

            X temp1 = xx.getValue();Y temp2 = temp1.operator Y();int tmp3 = yy.operator == (temp2);

            if(tmp3)  dosth```

            tmp2.Y::~Y();

            tmp1.X::~X();

            }

            surprise~~-----------------------------------------------------------------------

            6.1-對象的構造和析構

            ·一般來說,dtor會被放在每一個離開點(object 還存活)之前 ,所以,如果一個區段或者函數有多個離開點,那么每一個return 離開點都要插入一個dtor了。因此我們一般盡量放置object在使用它的程序區段附近,節省不必要的對象產生與摧毀操作。

            ·全局對象:全局對象如果有ctor或者dtor的話需要靜態的初始化操作和內存釋放操作。c++中全局對象放在data segment中,如果不明確指定值,內存內容為0.

            但是ctor必須等到程序startup后才能實施。由于必須對一個放在datasegment 中的object初始化表達式evaluate ,所以object需要靜態初始化

            一種策略(cfont 的 munch)

            為每一個需要靜態初始化的檔案產生一個  _sti()函數,內帶必要的ctor調用操作或者inline expansions。 類似的產澀會給你一個std()函數調用dtor

            一個_main()函數調用sti 一個 exit()函數調用_std()

            然后cfont在我們的程序中安插對 _main _exit 的調用。 最后需要解決的是如何收集各個對象的sti和std。cfont使用了nm命令 , 打印出符號表格(目標文件的符號表),然后munch會搜索所有用sti或者std開頭的目標函數,把他們記錄到一個表格,當main和exit調用時候便利表格即可。

            修改版本的方法是:system V中,coff格式的目標文件,檢驗可執行文件,找出有著_linknodes并且內帶一個指針指向 sti 和std函數的文件,把他們都串聯起來,接下來把鏈表頭結點設置為一個全局的_head object (定義在新的 patch runtime library),這個library中有一種不同的_main _exit 他們會以head為起點,遍歷鏈表,調用sti和std。

            實際上現在的ELF格式,有init 和.fini兩個section,完成靜態初始化和釋放操作。編譯器設定的startup函數會完成平臺特定的支持

            virtual base class 的指針或者引用存取virtual base class subobject,是一種只有在執行期才能加以確定的操作。所以,編譯器需要支持class object 的靜態初始化,至少涵蓋object的指針和reference。

            局部靜態對象

            const Matrix& identity(){

                static Matrix mat_identity;

                return mat_identity;

            }

            mat_identity的ctor必須只執行一次,mat_identity的dtor必須只執行一次

            編譯器只在identity被調用的時候才構造mat_identity,這樣避免如果不被調用也需要構造所有對象。同時編譯器引入條件式解析~也就是如果構造了則解析之

            對象數組:

            Points knots[10];

            如果Points沒有定義ctor和dtor只要分配空間即可

            如果有default ctor ,ctor必須實施于每個元素身上~這是由runtime library 完成的。 cfont中 我們使用vec_new()函數  MS和Sun提供兩個函數,一個用來處理 vbs的class 一個處理內帶base class 的class,后者為 vec_vnew() 函數原型基本如下

            void* vec_new(void *array,size_t elem_size,int elem_count,void (*ctor)(void*),void(*dtor)(void*,char)))

            array如果是0,數組由new分配與heap, vec_new(&knots,sizeof(Point),10,&Point::Point,0);

            6.2 new 和 delete 運算符

            int *pi  = new int(5);

            執行步驟:

            int* pi = __new (sizeof(int));

            *pi = 5;

            int *pi;

            if(pi = __new(sizeof(int)))

               *pi=5;

            delete pi;

            if(pi!=0)

            __delete (pi);

            注意pi并不會自動清除為0!

            CTOR

            Point3d * origin=new Point3d;

            if(origin = __new(sizeof(Point3d))){

            try{

               origin = Point3d::Point3d(origin);   

            }

            calch(…){

            __delete(origin);

            throw;//上傳exception

            }

            }

            DTOR

            delete origin;

            if(origin!=0){

              Point3d::~Point3d(origin);

               __delete(origin);

            }

            一種library對new的設計:~~

            extern void* operator new(size_t size){

            if(size==0)size=1;

            void *last_alloc;

            while(!(last_alloc=malloc(size))){

            if(_new_handler) (*_new_handler)();

            else return 0;

            }

            return last_alloc;

            }

            雖然new T[0];是合法的,但是語言要求每次對new的調用必須返回一個獨一無二的指針,解決該問題的傳統方法是傳回一個指針,指向默認為1byte的內存區塊。所以size被設為1.然后這種設計允許使用者提供一個屬于自己的_new_handler() 函數。

            extern void operator delete (void *ptr) { if(ptr)free (char*)ptr;}

            針對數組的new 語義:

            int *p_array = new int[5];

            這時候 vec_new()不會真正調用,因為,它的主要功能是把default ctor 實施于class object數組的每個元素身上。new運算符會被調用:

            int *p_array = (int*) __new(5*sizeof(int));

            如果數組的class object 有default ctor vec_new才會被調用。

            Point3d *p_array = new Point3d[10];編譯成:

            Point3d *p_array = vec_new(0,sizeof(Point3d),10,&point3d::Point3d,&Point3d::~Point3d);

            個別數組構造如果exception發生,dtor被傳輸給vec_new ,已經構造的object需要dtor 實施。

            delete 時候,開始需要程序員指定大小,后來編譯器開始不適用程序員指定的,而是只需要寫delete [] ptr 即可。

            如何記錄數組大小呢:

            一種方法在vecnew返回的內存塊配置一個額外的word,大小放在其中。

            如果

            class Point {public:virtual ~Point (){}};

            class Point3d : public Point {public:virtual ~Point3d(){}};

            如果Point *ptr = new Point3d[10];

            當我們delete [] ptr;時候只有 Point::~Point被掉用````

            在vc里敲了代碼驗證確實如此~~~

            實施于數組上的dtor是根據交給vec_delete()函數的被刪除指針類型的dtor,也就是point的dtor,每一個元素大小也被一起傳了過去。

            如何避免:

            避免一個base class 指針指向一個derived class 的數組。如果真的要這么寫看代碼吧

            class point{
            public:
                int p;
                point(){cout<<"point ctor"<<endl;}
                ~point(){cout<<"point dtor"<<endl;}
            };
            class point3d:public point{
            public:
                int q;
                point3d(){cout<<"point3d ctor"<<endl;}
                ~point3d(){cout<<"point3d dtor"<<endl;}
            };
            int main()
            {   
                 point *ptr = new point3d[3];
                 //delete [] ptr; 這樣寫是不行的

                 //要這樣寫
                 for(int i=0;i<3;i++){
                     point3d * p=&((point3d*)ptr)[i]; //恢復成point3d數組指針
                     delete p;
                 }
            }

            Placement Operator New

            有一個重載過的new 運算符 需要兩個參數,類型為void*

            Point2w *ptw = new(area) Point2w;

            其中area指向內存一個區塊,用來放置產生出來的Point2w object.這個預先定義好的placement operator new 實現方法: 將獲得的指針arena 所指的地址傳回即可

            void* operator new (size_t,void* p) {return p;}

            事實上,他執行的另一半工作是:把point2w 的ctor 實施于 arena所指的地址上

            Point2w *ptw = (Point2w *)arena; if(ptw!=0)ptw->Point2w::Point2w();

            -------

            p2w->~Point2w;

            p2w = new(arena)Point2w;

            如果我們用

            delete p2w; p2w = new(arena) Point2w;

            delete會釋放p2w指向的內存 由于下一指令還要用到p2w,我們應該調用dtor并保留存儲空間,以便再次使用.

            還有一些關于placement opeator new 的設計問題··沒看明白 不記了··

            6.3臨時對象 :

            c++對臨時對象并無硬性規定,由編譯器抉擇。

            實際上 T c = a+ b; T operator + (const T& ,const T&);

            a+b可以直接構建于c上

            那么根本不產生臨時對象

            但是,意義相當的 c=a+b;不能忽略臨時對象":

            T temp; temp=operator+(a,b);c.operator =(tmp);tmp.T::~T();

            注意c=a+b;中,直接傳遞c進入operator 中,也就是不要tmp的話:由于operator函數不為其外加參數調用dtor(期望一個新鮮的內存),所以必須在其調用前調用dtor.然而轉換操作會變成c.T::~T();c.T::T(a+b);copy ctor dtor  copy assignment operator 都可以自定義,所以我們用 析構和拷貝構造代替賦值一般而言是不安全的,所以需要臨時對象調用operator=

            所以 T c=a+b;比 c=a+b;更有效率

            臨時對象生命周期:

            臨時對象被摧毀,應該是對完整表達式求職過程的最后一個步驟,該完整表達式造成臨時對象的產生

            如果一個臨時對象綁定在一個reference上,對象將殘留,知道被初始化之reference生命結束,或者知道臨時對象的聲明范疇結束。

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            posted @ 2010-03-19 16:55 rikisand 閱讀(368) | 評論 (0)編輯 收藏

            僅列出標題  下一頁
            国内精品久久久久影院免费| 色婷婷狠狠久久综合五月| 国产午夜福利精品久久| 久久高潮一级毛片免费| 99久久99久久| 精品国产乱码久久久久久1区2区 | 伊人久久综合热线大杳蕉下载| 久久精品中文无码资源站| 99久久夜色精品国产网站| 久久综合九色综合网站| 国产亚洲精品久久久久秋霞| 99久久无色码中文字幕人妻| 亚洲香蕉网久久综合影视| 精品久久久久久久无码| 91精品国产高清久久久久久io| 国产精品一久久香蕉国产线看观看| 久久综合给合久久狠狠狠97色| 久久精品无码专区免费东京热| 人妻无码αv中文字幕久久| 成人资源影音先锋久久资源网| 亚洲国产精品久久久久婷婷软件| 久久99精品国产麻豆婷婷| 亚洲国产日韩欧美久久| 蜜臀久久99精品久久久久久小说| 97精品伊人久久大香线蕉app | 久久久久国产精品| 日韩久久无码免费毛片软件| 99精品国产综合久久久久五月天| 国产精品久久久久久| 久久久久人妻精品一区三寸蜜桃| 欧美国产成人久久精品| 97超级碰碰碰久久久久| 亚洲欧洲精品成人久久曰影片| 亚洲精品tv久久久久久久久| a级毛片无码兔费真人久久| 久久国产色av免费看| 久久精品一区二区国产| 一日本道伊人久久综合影| 九九99精品久久久久久| 丁香色欲久久久久久综合网| 2020最新久久久视精品爱|