POJ3436-ACM Computer Factory

來源:互聯網
上載者:User

 

轉載請註明出處:優YoU 

http://user.qzone.qq.com/289065406/blog/1299340266

提示:最大流問題

     折磨了我3天的題。。。網上的前輩都推薦拆點做,但是我沒有用拆點(感覺拆點很麻煩)

        這道題我用了三種方法去做,但是結果卻差強人意。。。。

         【BFS+標號法+不拆點】 成功AC

         【BFS+壓入重標法+不拆點】(WA,不知道錯哪裡了,找不到反例) 

         【BFS+壓入重標法+類比拆點】(WA,不知道錯哪裡了,找不到反例)     

AC的程式我貼下面,後兩個WA的代碼我貼在AC代碼下面,希望有達人幫我查出哪裡出錯了。。。無限感激

 

題意:

老實說,我完全看不懂題目在說什麼= =。。。Orz

不過還是簡單概括下:

有N台機器,每台機器有P部分,每部分都有各自的輸入、輸出規格,因此一台機器有P個輸入規格,P個輸出規格。每台機器有2*P+1種參數去描述:第一個參數Q:該機器的容量;接下來P個參數S:該機器各部分的輸入規格;接下來P個參數D:該機器各部分的輸出規格。

其中輸入規格有三種情況:0,1,2

0:該部分不能存在

1:該部分必須保留

2:該部分可有可無

輸出規格有2種情況:0,1

0:該部分不存在

1:該部分存在

 

至於這條題要我做什麼,我完全不理解= =

不過通過對範例的輸入輸出的剖析,再加之前人總結出來的一些不清不楚的見解,我能夠詳盡地分析這題的模型O(∩_∩)O哈哈~

 

注意:本題可以只有一次輸入,一次輸出,還有Sample I/O這段英文不用輸入輸出

Sample input:

P  N (N台機器,每台機器有P部分)

接著輸入N行,其實每行都是一個結點的資訊

每一行的格式為 一個Q  P個S  P個D

其中Q為當前結點的容量,S都是當前結點的輸入規格,D都是輸出規格

Sample output:

第一行的兩個數字分別表示:最大流的值,流量發生變化的邊數M(和s還有t關聯的邊不在其內,那些不屬於原有的邊,是附加邊)

接下來有M行,每一行都有三個數字,A B W

A B為流量發生變化的邊的端點,W為流量的變化值(每條邊初始流量為0,最終流量就是找到最大流時的流量)

 

若圖不連通,則輸出0 0

 

解題思路:

 首先構造圖:

添加兩個超級源s,超級匯t

  如果某個節點(i)的輸入部分不含1,則添加一條s->i路徑,容量為Qi;

  如果某個節點(j)輸出全為1,則添加一條j->t路徑,容量為Qj;

  如果節點i的輸出與j的輸入不存在衝突(輸出與輸入對應位置的和不能為1),則添加一條i->j的路徑,容量為min(Qi, Qj).

PS:輸出與輸入對應位置的和不能為1,就是說組合為可以為00,11, 21或20,但不能是01

解題方法:

就是最大流問題

 

/*BFS+不拆點 -> 成功AC*///Memory Time //292K   0MS #include<iostream>using namespace std;const int inf=10001;int s; //超級源int t;   //超級匯int n;  //總結點數(包括超級源、超級匯)int p;  //每台機器的部分數int cap[52][52];// 邊容量int min(int a,int b){return a<b?a:b;}/*利用BFS找增廣鏈求網路最大流*/int maxflow(void)  {int queue[52];int head,tail;int pre[52]; //結點i的前驅    int minflow;int flow = 0;    int x,y;    while(true)    {        memset(pre, -1, sizeof(pre));        for(queue[head=tail=0]=s;head<=tail;head++)        {            x=queue[head];            for(int i=0;(i<n) && (pre[t]==-1);i++)//當匯點還沒有被標記時               if (cap[x][i]>0 && pre[i]==-1)  //當結點u指向i的邊存在,且i還沒有標記前驅時               {                    pre[i]=x;//記錄結點i的前驅為u                    queue[++tail]=i;               }        }        if(pre[t]==-1)break;//BFS後匯點沒有被標記,則跳出while,已經不存在增廣鏈        minflow=inf;//初始化        for(x=pre[y=t];y!=s;)//回溯{if(cap[x][y] < minflow)minflow=cap[x][y];//尋找當前增廣鏈中最小容量的邊,記錄其邊權(容量)y=x;x=pre[y];}        for(x=pre[y=t];y!=s;) //當前增廣鏈 流量調整{cap[x][y] -= minflow;  //正向弧容量減少cap[y][x] += minflow;  //反向弧容量增加y=x;x=pre[y];}        flow += minflow;  //最大流=每次尋得的增廣鏈的調整量之和    }    return flow;//返回最大流}int main(int i,int j,int k){int in[52][21];int out[52][3];int backup[52][52];//備份圖    int N;  //除超級源、超級匯以外的總結點數int line;  //生產線數(容量發生變化的邊數)int flow;  //最大流    while (cin>>p>>N)    {/*Initial*/        memset(cap,0,sizeof(cap)); //所有正向弧和反向弧的容量都初始化為0        s=0;//超級源t=N+1; //超級匯        n=N+2; //總結點數+2line=0;  //記錄變化的邊的數量(生產線數量)/*Input*/        for(i=1;i<=N;i++)            for(j=0;j<2*p+1;j++)                cin>>in[i][j];    //用一個數列儲存第i個結點的資訊 in[i][0] 為結點i的容量        bool flag_s, flag_t;        for(i=1;i<=N;i++)        {            flag_s=flag_t=true;            for(k=0;k<p;k++)            {                if(in[i][1+k]==1)flag_s=false;  //檢查第i個結點的輸入序列資訊,當輸入列不含1時                if(in[i][p+1+k]==0)flag_t=false;//檢查第i個結點的輸出序列資訊,當輸出資料行全為1時            }            if(flag_s)cap[s][i]=in[i][0];  //當輸入列不含1時,S->i,邊容量為i的容量            if(flag_t)cap[i][t]=in[i][0]; //當輸出資料行全為1時,i->t,邊容量為i的容量            bool flag=true;            for(j=1;j<=N;j++)if(i!=j)                {                    flag=true;                    for(k=0;(k<p) && flag;k++)if(in[i][p+1+k]+in[j][1+k]==1)  //當第i個結點的第k個輸出位,對應第j個結點的第k個輸入位之和全不為0時                            flag=false;                    if(flag)cap[i][j] = min(in[i][0], in[j][0]);  //i->j,邊容量為i的容量和j的容量的最小值                }        }/*利用BFS找增廣鏈求網路最大流*/        memcpy(backup, cap, sizeof(cap));  //把尋找增廣鏈前的圖的容量資訊複製        flow=maxflow();  //返回最大流        /*Output*/        for(i=1;i<=N;i++)   //注意範圍,排除了含超級源和超級匯的邊for(j=1;j<=N;j++)                if (cap[i][j] < backup[i][j])//比較調整前後的邊權,若容量減少了,則輸出這條邊的資訊                {                    out[line][0]=i;     //i,j為生產線的兩端點                    out[line][1]=j;                    out[line][2]=backup[i][j] - cap[i][j];//變化的流量值(該生產線的最大生產量)                    line++;                }        cout<<flow<<' '<<line<<endl;        for(i=0;i<line;i++)            cout<<out[i][0]<<' '<<out[i][1]<<' '<<out[i][2]<<endl;    }    return 0;}

 

 

 

==========華麗的分割線===========

/*【BFS+壓入重標法+不拆點】-> WA */#include<iostream>using namespace std;const int inf=10001;int s=0; //超級源int t;   //超級匯int n;  //機器數int p;  //每台機器的部分數int cap[52][52];  //弧(i,j)的容量int flow[52][52];  //弧(i,j)的流量bool mark[52][52]={false};  int sum=0;bool vist[52];   //標記點i是否已標號class info   //當前點j的標記資訊{public:int pre;  //當前點j的前驅iint lv;  //l(v)int q;  //機器(結點i)的生產量(容量)int in[10];  //輸入規格int out[10]; //輸出規格int nei[51];  //當前結點直接指向的鄰居結點int pn;  //鄰居結點的指標}node[52];int min(int a,int b){return a<b?a:b;}void back(void){int x=t;while(x!=s){if(x!=t && node[x].pre!=s){if(!mark[ node[x].pre ][x])sum++;           //記錄流量發生變化的弧數(含s、t的弧除外)mark[ node[x].pre ][x]=true;  //標記弧(i,j)的流量是否發生了變化(含s、t的弧除外)}flow[ node[x].pre ][x] += node[t].lv;  //改進增廣路x=node[x].pre;}return;}bool bfs(void){memset(vist,false,sizeof(vist));vist[s]=true;int queue[52];int head=0;int tail=0;queue[tail++]=s;while(head<=tail-1)  //注意,這是再也找不到增廣路的結束條件{int x=queue[head];int y;for(int i=0;i<node[x].pn;i++){y=node[x].nei[i];if(!vist[y] && flow[x][y]<cap[x][y])   //搜尋的目標要求是 未標記 & 非飽和弧{queue[tail++]=y;vist[y]=true;node[y].pre=x;node[y].lv=min( node[x].lv , cap[x][y]-flow[x][y] );}if(vist[t])  //當超級匯被標記break;}if(!vist[t])head++;elsereturn true;  //搜尋到一條增廣路}return false;}int main(int i,int j,int k){/*Input*/cin>>p>>n;/*Initial*/node[s].pre=-1;node[s].lv=inf;t=n+1;for(i=0;i<t;i++)node[i].pn=0;/*Input & Structure Graphs*/bool sign;for(i=1;i<=n;i++){sign=false;cin>>node[i].q;for(j=0;j<p;j++){cin>>node[i].in[j];if(node[i].in[j]==1)  //如果某個節點(i)的輸入部分不含1sign=true;}if(!sign)   //則添加一條s->i路徑,容量為Qi{node[s].nei[ node[s].pn++ ]=i;cap[s][i]=node[i].q;flow[s][i]=0;}sign=false;for(j=0;j<p;j++){cin>>node[i].out[j];if(node[i].out[j]==0)  //如果某個節點(j)輸出全為1sign=true;}if(!sign)  //則添加一條j->t路徑,容量為Qj{node[i].nei[ node[i].pn++ ]=t;cap[i][t]=node[i].q;flow[i][t]=0;}}for(i=1;i<=n;i++)for(j=1;j<=n;j++){sign=false;if(i!=j){for(k=0;k<p;k++)if((node[i].out[k] + node[j].in[k])==1)  //如果節點i的輸出與j的輸入不存在衝突{                                      //即輸出與輸入對應位置的和不為1sign=true;break;}if(!sign)        //則添加一條i->j的路徑,容量為min(Qi, Qj).{node[i].nei[ node[i].pn++ ]=j;cap[i][j]=min(node[i].q,node[j].q);flow[i][j]=0;}}}/*壓入重標法找增廣軌*/while(true){if(bfs())  //如果能搜到到增廣路back();  //從超級匯開始回溯,改進增廣路else{int max=0;for(i=0;i<node[s].pn;i++)max+=flow[s][ node[s].nei[i] ];cout<<max<<' '<<sum<<endl;for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(i!=j && mark[i][j])cout<<i<<' '<<j<<' '<<flow[i][j]<<endl;break;}}return 0;}

 

 

==========華麗的分割線===========

/*【BFS+壓入重標法+類比拆點】-> WA */#include<iostream>using namespace std;const int inf=10001;int s=0; //超級源int t;   //超級匯int n;  //機器數int p;  //每台機器的部分數int cap[52][52];  //弧(i,j)的容量int flow[52][52];  //弧(i,j)的流量bool mark[52][52]={false};  int sum=0;bool vist[52];   //標記點i是否已標號class info   //當前點j的標記資訊{public:int pre;  //當前點j的前驅iint lv;  //l(v)int q;  //機器(結點j)的總生產量(容量)int f;  //機器(結點j)的當前生產量(流量)int in[10];  //輸入規格int out[10]; //輸出規格int nei[51];  //當前結點直接指向的鄰居結點int pn;  //鄰居結點的指標}node[52];int min(int a,int b){return a<b?a:b;}void back(void){int x=t;while(x!=s){if(x!=t && node[x].pre!=s){if(!mark[ node[x].pre ][x])sum++;           //記錄流量發生變化的弧數(含s、t的弧除外)mark[ node[x].pre ][x]=true;  //標記弧(i,j)的流量是否發生了變化(含s、t的弧除外)}flow[ node[x].pre ][x] += node[t].lv;  //改進增廣路node[x].f += node[t].lv;        //改進增廣路上的頂點x=node[x].pre;}return;}bool bfs(void){memset(vist,false,sizeof(vist));vist[s]=true;int queue[52];int head=0;int tail=0;queue[tail++]=s;while(head<=tail-1)  //注意,這是再也找不到增廣路的結束條件{int x=queue[head];int y;for(int i=0;i<node[x].pn;i++){y=node[x].nei[i];if(!vist[y] && flow[x][y]<cap[x][y] && node[y].f<node[y].q)   //搜尋的目標要求是 未標記 & 非飽和弧 & 非飽和點(類比拆點){                                                             //當某一頂點滿流後,該頂點不能再生產更多的機器queue[tail++]=y;vist[y]=true;node[y].pre=x;node[y].lv=min( node[x].lv , cap[x][y]-flow[x][y] );}if(vist[t])  //當超級匯被標記break;}if(!vist[t])head++;elsereturn true;  //搜尋到一條增廣路}return false;}int main(int i,int j,int k){freopen("in.txt","r",stdin);/*Input*/cin>>p>>n;/*Initial*/ t=n+1;node[s].pre=-1;node[s].lv=inf;node[t].q=inf;for(i=0;i<=t;i++){node[i].pn=0;node[i].f=0;}/*Input & Structure Graphs*/bool sign;for(i=1;i<=n;i++){sign=false;cin>>node[i].q;for(j=0;j<p;j++){cin>>node[i].in[j];if(node[i].in[j]==1)  //如果某個節點(i)的輸入部分不含1sign=true;}if(!sign)   //則添加一條s->i路徑,容量為Qi{node[s].nei[ node[s].pn++ ]=i;cap[s][i]=node[i].q;flow[s][i]=0;}sign=false;for(j=0;j<p;j++){cin>>node[i].out[j];if(node[i].out[j]==0)  //如果某個節點(j)輸出全為1sign=true;}if(!sign)  //則添加一條j->t路徑,容量為Qj{node[i].nei[ node[i].pn++ ]=t;cap[i][t]=node[i].q;flow[i][t]=0;}}for(i=1;i<=n;i++)for(j=1;j<=n;j++){sign=false;if(i!=j){for(k=0;k<p;k++)if((node[i].out[k] + node[j].in[k])==1)  //如果節點i的輸出與j的輸入不存在衝突{                                      //即輸出與輸入對應位置的和不為1sign=true;break;}if(!sign)        //則添加一條i->j的路徑,容量為min(Qi, Qj).{node[i].nei[ node[i].pn++ ]=j;cap[i][j]=min(node[i].q,node[j].q);flow[i][j]=0;}}}/*壓入重標法找增廣軌*/while(true){if(bfs())  //如果能搜到到增廣路back();  //從超級匯開始回溯,改進增廣路else{int max=0;for(i=0;i<node[s].pn;i++)max+=flow[s][ node[s].nei[i] ];cout<<max<<' '<<sum<<endl;for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(i!=j && mark[i][j])cout<<i<<' '<<j<<' '<<flow[i][j]<<endl;break;}}return 0;}

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.