zoukankan      html  css  js  c++  java
  • POJ3436ACM Computer Factory

    转载请注明出处:優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  PS  PD

    其中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,就是说组合为可以为0011 2120,但不能是01

    解题方法:

    就是最大流问题

     

      1 /*【BFS+标号法+不拆点】 成功AC*/
    2
    3 //Memory Time
    4 //292K 0MS
    5
    6 #include<iostream>
    7 using namespace std;
    8
    9 const int inf=10001;
    10 int s; //超级源
    11 int t; //超级汇
    12
    13 int n; //总结点数(包括超级源、超级汇)
    14 int p; //每台机器的部分数
    15 int cap[52][52];// 边容量
    16
    17
    18 int min(int a,int b)
    19 {
    20 return a<b?a:b;
    21 }
    22
    23 /*利用BFS找增广链求网络最大流*/
    24
    25 int maxflow(void)
    26 {
    27 int queue[52];
    28 int head,tail;
    29 int pre[52]; //结点i的前驱
    30
    31 int minflow;
    32 int flow = 0;
    33 int x,y;
    34
    35 while(true)
    36 {
    37 memset(pre, -1, sizeof(pre));
    38
    39 for(queue[head=tail=0]=s;head<=tail;head++)
    40 {
    41 x=queue[head];
    42 for(int i=0;(i<n) && (pre[t]==-1);i++)//当汇点还没有被标记时
    43 if (cap[x][i]>0 && pre[i]==-1) //当结点u指向i的边存在,且i还没有标记前驱时
    44 {
    45 pre[i]=x;//记录结点i的前驱为u
    46 queue[++tail]=i;
    47 }
    48 }
    49
    50 if(pre[t]==-1)
    51 break;//BFS后汇点没有被标记,则跳出while,已经不存在增广链
    52
    53 minflow=inf;//初始化
    54 for(x=pre[y=t];y!=s;)//回溯
    55 {
    56 if(cap[x][y] < minflow)
    57 minflow=cap[x][y];//寻找当前增广链中最小容量的边,记录其边权(容量)
    58 y=x;
    59 x=pre[y];
    60 }
    61
    62 for(x=pre[y=t];y!=s;) //当前增广链 流量调整
    63 {
    64 cap[x][y] -= minflow; //正向弧容量减少
    65 cap[y][x] += minflow; //反向弧容量增加
    66 y=x;
    67 x=pre[y];
    68 }
    69
    70 flow += minflow; //最大流=每次寻得的增广链的调整量之和
    71 }
    72 return flow;//返回最大流
    73 }
    74
    75 int main(int i,int j,int k)
    76 {
    77 int in[52][21];
    78 int out[52][3];
    79 int backup[52][52];//备份图
    80 int N; //除超级源、超级汇以外的总结点数
    81 int line; //生产线数(容量发生变化的边数)
    82 int flow; //最大流
    83
    84 while (cin>>p>>N)
    85 {
    86 /*Initial*/
    87
    88 memset(cap,0,sizeof(cap)); //所有正向弧和反向弧的容量都初始化为0
    89
    90 s=0;//超级源
    91 t=N+1; //超级汇
    92 n=N+2; //总结点数+2
    93 line=0; //记录变化的边的数量(生产线数量)
    94
    95 /*Input*/
    96
    97 for(i=1;i<=N;i++)
    98 for(j=0;j<2*p+1;j++)
    99 cin>>in[i][j]; //用一个数列存储第i个结点的信息 in[i][0] 为结点i的容量
    100
    101 bool flag_s, flag_t;
    102 for(i=1;i<=N;i++)
    103 {
    104 flag_s=flag_t=true;
    105 for(k=0;k<p;k++)
    106 {
    107 if(in[i][1+k]==1)
    108 flag_s=false; //检查第i个结点的输入序列信息,当输入列不含1时
    109 if(in[i][p+1+k]==0)
    110 flag_t=false;//检查第i个结点的输出序列信息,当输出列全为1时
    111 }
    112 if(flag_s)
    113 cap[s][i]=in[i][0]; //当输入列不含1时,S->i,边容量为i的容量
    114 if(flag_t)
    115 cap[i][t]=in[i][0]; //当输出列全为1时,i->t,边容量为i的容量
    116
    117 bool flag=true;
    118 for(j=1;j<=N;j++)
    119 if(i!=j)
    120 {
    121 flag=true;
    122 for(k=0;(k<p) && flag;k++)
    123 if(in[i][p+1+k]+in[j][1+k]==1) //当第i个结点的第k个输出位,对应第j个结点的第k个输入位之和全不为0时
    124 flag=false;
    125
    126 if(flag)
    127 cap[i][j] = min(in[i][0], in[j][0]); //i->j,边容量为i的容量和j的容量的最小值
    128 }
    129 }
    130
    131 /*利用BFS找增广链求网络最大流*/
    132
    133 memcpy(backup, cap, sizeof(cap)); //把寻找增广链前的图的容量信息复制
    134 flow=maxflow(); //返回最大流
    135
    136 /*Output*/
    137
    138 for(i=1;i<=N;i++) //注意范围,排除了含超级源和超级汇的边
    139 for(j=1;j<=N;j++)
    140 if (cap[i][j] < backup[i][j])//比较调整前后的边权,若容量减少了,则输出这条边的信息
    141 {
    142 out[line][0]=i; //i,j为生产线的两端点
    143 out[line][1]=j;
    144 out[line][2]=backup[i][j] - cap[i][j];//变化的流量值(该生产线的最大生产量)
    145 line++;
    146 }
    147
    148 cout<<flow<<' '<<line<<endl;
    149 for(i=0;i<line;i++)
    150 cout<<out[i][0]<<' '<<out[i][1]<<' '<<out[i][2]<<endl;
    151 }
    152 return 0;
    153 }

     

    ==========华丽的分割线===========

    View Code
      1 /*BFS+压入重标法+不拆点 ->WA*/
    2
    3 #include<iostream>
    4 using namespace std;
    5
    6 const int inf=10001;
    7 int s=0; //超级源
    8 int t; //超级汇
    9
    10 int n; //机器数
    11 int p; //每台机器的部分数
    12 int cap[52][52]; //弧(i,j)的容量
    13 int flow[52][52]; //弧(i,j)的流量
    14 bool mark[52][52]={false};
    15 int sum=0;
    16 bool vist[52]; //标记点i是否已标号
    17
    18 class info //当前点j的标记信息
    19 {
    20 public:
    21 int pre; //当前点j的前驱i
    22 int lv; //l(v)
    23 int q; //机器(结点i)的生产量(容量)
    24 int in[10]; //输入规格
    25 int out[10]; //输出规格
    26 int nei[51]; //当前结点直接指向的邻居结点
    27 int pn; //邻居结点的指针
    28 }node[52];
    29
    30 int min(int a,int b)
    31 {
    32 return a<b?a:b;
    33 }
    34
    35 void back(void)
    36 {
    37 int x=t;
    38 while(x!=s)
    39 {
    40 if(x!=t && node[x].pre!=s)
    41 {
    42 if(!mark[ node[x].pre ][x])
    43 sum++; //记录流量发生变化的弧数(含s、t的弧除外)
    44 mark[ node[x].pre ][x]=true; //标记弧(i,j)的流量是否发生了变化(含s、t的弧除外)
    45 }
    46 flow[ node[x].pre ][x] += node[t].lv; //改进增广路
    47 x=node[x].pre;
    48
    49 }
    50 return;
    51 }
    52
    53 bool bfs(void)
    54 {
    55 memset(vist,false,sizeof(vist));
    56 vist[s]=true;
    57
    58 int queue[52];
    59 int head=0;
    60 int tail=0;
    61 queue[tail++]=s;
    62
    63 while(head<=tail-1) //注意,这是再也找不到增广路的结束条件
    64 {
    65 int x=queue[head];
    66 int y;
    67 for(int i=0;i<node[x].pn;i++)
    68 {
    69 y=node[x].nei[i];
    70 if(!vist[y] && flow[x][y]<cap[x][y]) //搜索的目标要求是 未标记 & 非饱和弧
    71 {
    72 queue[tail++]=y;
    73
    74 vist[y]=true;
    75 node[y].pre=x;
    76 node[y].lv=min( node[x].lv , cap[x][y]-flow[x][y] );
    77 }
    78 if(vist[t]) //当超级汇被标记
    79 break;
    80 }
    81 if(!vist[t])
    82 head++;
    83 else
    84 return true; //搜索到一条增广路
    85 }
    86 return false;
    87 }
    88
    89 int main(int i,int j,int k)
    90 {
    91 /*Input*/
    92
    93 cin>>p>>n;
    94
    95 /*Initial*/
    96
    97 node[s].pre=-1;
    98 node[s].lv=inf;
    99 t=n+1;
    100 for(i=0;i<t;i++)
    101 node[i].pn=0;
    102
    103 /*Input & Structure Graphs*/
    104
    105 bool sign;
    106 for(i=1;i<=n;i++)
    107 {
    108 sign=false;
    109 cin>>node[i].q;
    110
    111 for(j=0;j<p;j++)
    112 {
    113 cin>>node[i].in[j];
    114 if(node[i].in[j]==1) //如果某个节点(i)的输入部分不含1
    115 sign=true;
    116 }
    117 if(!sign) //则添加一条s->i路径,容量为Qi
    118 {
    119 node[s].nei[ node[s].pn++ ]=i;
    120 cap[s][i]=node[i].q;
    121 flow[s][i]=0;
    122 }
    123
    124 sign=false;
    125 for(j=0;j<p;j++)
    126 {
    127 cin>>node[i].out[j];
    128 if(node[i].out[j]==0) //如果某个节点(j)输出全为1
    129 sign=true;
    130 }
    131 if(!sign) //则添加一条j->t路径,容量为Qj
    132 {
    133 node[i].nei[ node[i].pn++ ]=t;
    134 cap[i][t]=node[i].q;
    135 flow[i][t]=0;
    136 }
    137 }
    138
    139 for(i=1;i<=n;i++)
    140 for(j=1;j<=n;j++)
    141 {
    142 sign=false;
    143 if(i!=j)
    144 {
    145 for(k=0;k<p;k++)
    146 if((node[i].out[k] + node[j].in[k])==1) //如果节点i的输出与j的输入不存在冲突
    147 { //即输出与输入对应位置的和不为1
    148 sign=true;
    149 break;
    150 }
    151
    152 if(!sign) //则添加一条i->j的路径,容量为min(Qi, Qj).
    153 {
    154 node[i].nei[ node[i].pn++ ]=j;
    155 cap[i][j]=min(node[i].q,node[j].q);
    156 flow[i][j]=0;
    157 }
    158 }
    159 }
    160
    161 /*压入重标法找增广轨*/
    162
    163 while(true)
    164 {
    165 if(bfs()) //如果能搜到到增广路
    166 back(); //从超级汇开始回溯,改进增广路
    167 else
    168 {
    169 int max=0;
    170 for(i=0;i<node[s].pn;i++)
    171 max+=flow[s][ node[s].nei[i] ];
    172 cout<<max<<' '<<sum<<endl;
    173 for(i=1;i<=n;i++)
    174 for(j=1;j<=n;j++)
    175 if(i!=j && mark[i][j])
    176 cout<<i<<' '<<j<<' '<<flow[i][j]<<endl;
    177 break;
    178 }
    179 }
    180 return 0;
    181 }

     

    ==========华丽的分割线===========

     

    View Code
      1 /*BFS+压入重标法+模拟拆点  ->WA*/
    2
    3 #include<iostream>
    4 using namespace std;
    5
    6 const int inf=10001;
    7 int s=0; //超级源
    8 int t; //超级汇
    9
    10 int n; //机器数
    11 int p; //每台机器的部分数
    12 int cap[52][52]; //弧(i,j)的容量
    13 int flow[52][52]; //弧(i,j)的流量
    14 bool mark[52][52]={false};
    15 int sum=0;
    16 bool vist[52]; //标记点i是否已标号
    17
    18 class info //当前点j的标记信息
    19 {
    20 public:
    21 int pre; //当前点j的前驱i
    22 int lv; //l(v)
    23 int q; //机器(结点j)的总生产量(容量)
    24 int f; //机器(结点j)的当前生产量(流量)
    25 int in[10]; //输入规格
    26 int out[10]; //输出规格
    27 int nei[51]; //当前结点直接指向的邻居结点
    28 int pn; //邻居结点的指针
    29 }node[52];
    30
    31 int min(int a,int b)
    32 {
    33 return a<b?a:b;
    34 }
    35
    36 void back(void)
    37 {
    38 int x=t;
    39 while(x!=s)
    40 {
    41 if(x!=t && node[x].pre!=s)
    42 {
    43 if(!mark[ node[x].pre ][x])
    44 sum++; //记录流量发生变化的弧数(含s、t的弧除外)
    45 mark[ node[x].pre ][x]=true; //标记弧(i,j)的流量是否发生了变化(含s、t的弧除外)
    46 }
    47 flow[ node[x].pre ][x] += node[t].lv; //改进增广路
    48 node[x].f += node[t].lv; //改进增广路上的顶点
    49 x=node[x].pre;
    50
    51 }
    52 return;
    53 }
    54
    55 bool bfs(void)
    56 {
    57 memset(vist,false,sizeof(vist));
    58 vist[s]=true;
    59
    60 int queue[52];
    61 int head=0;
    62 int tail=0;
    63 queue[tail++]=s;
    64
    65 while(head<=tail-1) //注意,这是再也找不到增广路的结束条件
    66 {
    67 int x=queue[head];
    68 int y;
    69 for(int i=0;i<node[x].pn;i++)
    70 {
    71 y=node[x].nei[i];
    72 if(!vist[y] && flow[x][y]<cap[x][y] && node[y].f<node[y].q) //搜索的目标要求是 未标记 & 非饱和弧 & 非饱和点(模拟拆点)
    73 { //当某一顶点满流后,该顶点不能再生产更多的机器
    74 queue[tail++]=y;
    75
    76 vist[y]=true;
    77 node[y].pre=x;
    78 node[y].lv=min( node[x].lv , cap[x][y]-flow[x][y] );
    79 }
    80 if(vist[t]) //当超级汇被标记
    81 break;
    82 }
    83 if(!vist[t])
    84 head++;
    85 else
    86 return true; //搜索到一条增广路
    87 }
    88 return false;
    89 }
    90
    91 int main(int i,int j,int k)
    92 {
    93 freopen("in.txt","r",stdin);
    94
    95 /*Input*/
    96
    97 cin>>p>>n;
    98
    99 /*Initial*/
    100
    101 t=n+1;
    102 node[s].pre=-1;
    103 node[s].lv=inf;
    104 node[t].q=inf;
    105 for(i=0;i<=t;i++)
    106 {
    107 node[i].pn=0;
    108 node[i].f=0;
    109 }
    110
    111 /*Input & Structure Graphs*/
    112
    113 bool sign;
    114 for(i=1;i<=n;i++)
    115 {
    116 sign=false;
    117 cin>>node[i].q;
    118
    119 for(j=0;j<p;j++)
    120 {
    121 cin>>node[i].in[j];
    122 if(node[i].in[j]==1) //如果某个节点(i)的输入部分不含1
    123 sign=true;
    124 }
    125 if(!sign) //则添加一条s->i路径,容量为Qi
    126 {
    127 node[s].nei[ node[s].pn++ ]=i;
    128 cap[s][i]=node[i].q;
    129 flow[s][i]=0;
    130 }
    131
    132 sign=false;
    133 for(j=0;j<p;j++)
    134 {
    135 cin>>node[i].out[j];
    136 if(node[i].out[j]==0) //如果某个节点(j)输出全为1
    137 sign=true;
    138 }
    139 if(!sign) //则添加一条j->t路径,容量为Qj
    140 {
    141 node[i].nei[ node[i].pn++ ]=t;
    142 cap[i][t]=node[i].q;
    143 flow[i][t]=0;
    144 }
    145 }
    146
    147 for(i=1;i<=n;i++)
    148 for(j=1;j<=n;j++)
    149 {
    150 sign=false;
    151 if(i!=j)
    152 {
    153 for(k=0;k<p;k++)
    154 if((node[i].out[k] + node[j].in[k])==1) //如果节点i的输出与j的输入不存在冲突
    155 { //即输出与输入对应位置的和不为1
    156 sign=true;
    157 break;
    158 }
    159
    160 if(!sign) //则添加一条i->j的路径,容量为min(Qi, Qj).
    161 {
    162 node[i].nei[ node[i].pn++ ]=j;
    163 cap[i][j]=min(node[i].q,node[j].q);
    164 flow[i][j]=0;
    165 }
    166 }
    167 }
    168
    169 /*压入重标法找增广轨*/
    170
    171 while(true)
    172 {
    173 if(bfs()) //如果能搜到到增广路
    174 back(); //从超级汇开始回溯,改进增广路
    175 else
    176 {
    177 int max=0;
    178 for(i=0;i<node[s].pn;i++)
    179 max+=flow[s][ node[s].nei[i] ];
    180 cout<<max<<' '<<sum<<endl;
    181 for(i=1;i<=n;i++)
    182 for(j=1;j<=n;j++)
    183 if(i!=j && mark[i][j])
    184 cout<<i<<' '<<j<<' '<<flow[i][j]<<endl;
    185 break;
    186 }
    187 }
    188 return 0;
    189 }

  • 相关阅读:
    node
    前端工程师的思考
    前端工程师需要具备的条件
    产品经理必须掌握的名词
    金融人必须掌握的词汇
    央企降两金、降杠杆的 “兵器谱”
    抽屉协议
    清分、清算、结算的关系
    如何有效规避风险
    浅谈在项目管理过程中风险管理
  • 原文地址:https://www.cnblogs.com/lyy289065406/p/2122119.html
Copyright © 2011-2022 走看看