poj 1815 最大流SAP优化_codestorm_新浪博客

本文详细介绍了如何通过图论的方法,求解从源点到汇点的最小点割集,包括将点割集转化为边割集、使用增广路径算法寻找最大流以确定最小割等关键步骤。同时,文章提出了一种贪心策略来找出分数最小的点割集,并通过代码实例进行了详细解释。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://blog.youkuaiyun.com/scorpiocj/article/details/6070518

题目模型就是从s到t的连通图中,最少删除多少个点,可以使s和t不连通,也就是求一个最小点割集(最小点割集的概念见下面的链接)http://hi.baidu.com/492943457/blog/item/1401d1a964a3cdbfcb130c89.html将s看成源点,t看成汇点,有结论:源点到汇点的最小点割集的大小等于图中最多的点不相交路径数目

做法:将每个点拆成两个点u和u',之间连容量为1的边,原来从u到v的边,变成从u'到v的边,边容量都是无穷大,新源点为s',新汇点还是t这样,就将点不相交问题转化成边不相交问题,每条点不相交路径和一条边不相交路径一一对应。当有流值v时,意味着有v条边不相交路径(容量为1的限制),任意割的容量p>=v,每条边不相交路径中必有一条边是割边,当割是最小割时,p=v,v为最大流,这时,最小点割集的大小就是最小边割集的大小也就是最大流

于是,当源点和汇点不直接相连的情况下,我们就得到了我们的第一个答案但是题目要求很高,需要输出分数最小的点割集,即按字典序排序最小,我们可以贪心地从最小标号的顶点到最大标号的顶点一次枚举,每次删除边i和i',如果新的最大流比原来的最大流小,说明该点在最小点割集中,否则,恢复边i和i'(不然可能将原来不是割点的点变成割点)

 

代码:

 

  1. #include  
  2. #include  
  3. #include  
  4. using namespace std;  
  5. const int inf=1<<30;  
  6. const int MAX=405;  
  7. int c[MAX][MAX],f[MAX][MAX],rc[MAX],pre[MAX],q[MAX];  
  8. int dis[MAX],num[MAX],tmp1[MAX],tmp2[MAX],ans[MAX],cur[MAX];  
  9. int s,t,n,max_flow,cnt;  
  10. void bfs()  
  11. {  
  12.     int u,v,i,vis[MAX];  
  13.     memset(dis,0,sizeof(dis));  
  14.     memset(num,0,sizeof(num));  
  15.     queue<<span class="datatypes" style="margin: 0px; padding: 0px; border: none; color: rgb(46, 139, 87); background-color: inherit; font-weight: bold;">int>q;  
  16.     q.push(t);  
  17.     vis[t]=0;  
  18.     dis[t]=0;  
  19.     num[0]++;  
  20.     while(!q.empty())  
  21.     {  
  22.         u=q.front();  
  23.         q.pop();  
  24.         for(v=1;v<=2*n;v++)  
  25.         {  
  26.             if(c[v][u]&&!vis[u])  
  27.             {  
  28.                 dis[v]=dis[v]+1;  
  29.                 num[dis[v]]++;  
  30.                 vis[v]=1;  
  31.                 q.push(v);  
  32.             }  
  33.         }  
  34.     }  
  35. }  
  36. int sap()  
  37. {  
  38.     int u,v,i,aug=inf,flag,flow=0;  
  39.     for(i=1;i<=2*n;i++)  
  40.     {  
  41.         dis[i]=num[i]=0;  
  42.         cur[i]=1;  
  43.     }  
  44.     bfs();  
  45.     u=s;  
  46.     num[0]=2*n;  
  47.     while(dis[s]<2*n)  
  48.     {  
  49.         flag=0;  
  50.         for(v=cur[u];v<=2*n;v++)  
  51.         {  
  52.             if(c[u][v]>f[u][v]&&dis[u]==dis[v]+1)  
  53.             {  
  54.                 flag=1;  
  55.                 pre[v]=u;  
  56.                 cur[u]=v;  
  57.                 aug=min(aug,c[u][v]-f[u][v]);  
  58.                 u=v;  
  59.                 if(u==t)  
  60.                 {  
  61.                     flow+=aug;  
  62.                     while(u!=s)  
  63.                     {  
  64.                         f[pre[u]][u]+=aug;  
  65.                         f[u][pre[u]]-=aug;  
  66.                         u=pre[u];  
  67.                     }  
  68.                     aug=inf;  
  69.                     u=s;  
  70.                 }  
  71.                 break;  
  72.             }  
  73.         }  
  74.         if(flag)  
  75.             continue;  
  76.         if(--num[dis[u]]==0)  
  77.             return flow;  
  78.         dis[u]=inf;  
  79.         for(v=1;v<=2*n;v++)  
  80.         {  
  81.             if(c[u][v]>f[u][v]&&dis[v]
  82.             {  
  83.                 dis[u]=dis[v];  
  84.                 cur[u]=v;  
  85.             }  
  86.         }  
  87.         dis[u]++;  
  88.         num[dis[u]]++;  
  89.         if(u!=s)  
  90.             u=pre[u];  
  91.     }  
  92.     return flow;  
  93. }  
  94.               
  95. int main()  
  96. {  
  97.     int i,j,x,flag=0,cur_flow;  
  98.     scanf("%d%d%d",&n,&s,&t);  
  99.     s=s+n;//新源点   
  100.     for(i=1;i<=n;i++)  
  101.     {  
  102.         c[i][i+n]=1;  
  103.     }  
  104.     for(i=1;i<=n;i++)  
  105.     {  
  106.         for(j=1;j<=n;j++)  
  107.         {  
  108.             scanf("%d",&x);  
  109.             if(x)  
  110.             {  
  111.                 if(i==s-n&&j==t)  
  112.                     flag=1;  
  113.                 c[i+n][j]=inf;  
  114.             }  
  115.         }  
  116.     }  
  117.     if(flag)  
  118.     {  
  119.         printf("NO ANSWER!/n");  
  120.         return 0;  
  121.     }  
  122.     cnt=0;  
  123.     max_flow=sap();  
  124.     for(i=1;i<=n;i++)  
  125.     {  
  126.         if(i==s-n||i==t)  
  127.             continue;  
  128.         c[i][i+n]=0;  
  129.         memset(f,0,sizeof(f));//不要忘记这一步!!!   
  130.         cur_flow=sap();  
  131.         if(cur_flow
  132.         {  
  133.             cnt++;  
  134.             ans[cnt]=i;  
  135.             max_flow--;  
  136.         }  
  137.         else  
  138.         {  
  139.             c[i][i+n]=1;  
  140.         }  
  141.         if(max_flow<=0)  
  142.             break;  
  143.     }  
  144.     cout<<cnt<<endl;  
  145.     for(i=1;i<=cnt;i++)  
  146.     {  
  147.         if(i!=cnt)  
  148.             printf("%d ",ans[i]);  
  149.         else  
  150.             printf("%d",ans[i]);   
  151.     }  
  152.     printf("/n");  
  153.     return 0;  
  154. }  

批注:
1.将点割集转化为边割集,拆点后有向图,原先的边为inf,点之间为1
2.运用gap优化,记录每个dis的点数目,当某dis为0条时出现“断层”,直接结束
3.运用cur当前弧优化,避免每次都从0-->2*n寻找相邻边
4.增广路径算法找最大流,进而找到最小割,进而找到割点集。在寻找增广路径时找最短路径,若用dijkstra则复杂度很高。





 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值