POJ 3469 构图最小割+链表SAP

来源:互联网 发布:华为和星环大数据 编辑:程序博客网 时间:2024/05/20 07:20

题目大意:

有一台双核处理器的电脑,现有很多模块,每个模块放在不同的核中都有一个花销。若某两个模块不放在同一个核中执行,那么会产生额外的花销。
求出使得所有的模块都进入处理器中的总花销值最小。

首先可以明确的是,该题为二分图,模块放入两个核中。

如果没有惩罚。

那么该题就是赤裸裸的最小割(其实是贪心)。

源点S与Acpu中的点连边,容量为模块放在A核中的费用。

B核中的点与汇点连边,容量为模块放在B核中的花销。

做一次最大流,流出的值即为最小割。

现在考虑产生额外的花销。若u,v不放在一个集合中。则会产生额外的花销,在图中,用一条增广路来表示。

(由于没有在自己电脑上码畜,PPT不好用就不配图了)

做完增广路后的图中,(u,u')边,两个端点至少有一个满流。对于(v,v')边同理。

若两边同时在源点或汇点满流,则惩罚不存在。

所以,要有惩罚,必须一点源点边满流,另一点汇点边满流。

而且满流边一定是割边。割边是肯定保留的,也就是不需要改变状态。

要改变状态的就是那些还没有流满的边,这些边才会有可行流。

若可行流的值(源点与汇点的可行流)大于额外花销,使得额外花销边满流,代表直接惩罚。

若可行流的值等于额外花销,则使得可行流至少一条边流满,流满的边代表模块改变核。

若可行流的值小于额外花销,则使得可行流流流满,代表状态改变。

所以 我们可以在u与v'和v与u'直接连边,容量为惩罚值。

至此初步构图完成。

现在援引这里的文章

这是这篇文章中很好的一个中间结论,我们可以把这个结论放置于很多网络流的题目中,进行点的合并。

在这题中可以加一条,若两点合并不影响其他点,则可以合并= =... 算个伪结论吧.....

这题可以把拆开的点u,u'合并成u点。

以下SAP板子为HH神的板....

#include<iostream>#include<string>#include<cstdio>#include<cstdlib>#define MN 22222#define MM 999999#define INF 0x7FFFFFFF#define FF(i,a) for( int i=0;i<a;i++ )#define CC(a) memset( a,0,sizeof(a) )template<class T>inline void checkmin( T &a,T b ){ if( a>b||a==-1 ) a=b; } using namespace std;struct edge{   int pos,cap,next;}E[MM];int N,M,s,t,alloc;int head[MN],pre[MN],cur[MN],dis[MN],gap[MN];void addEdge( int u,int v,int c,int cc=0 ){     E[alloc].pos=v;     E[alloc].cap=c;     E[alloc].next=head[u];     head[u]=alloc++;          E[alloc].pos=u;     E[alloc].cap=cc;     E[alloc].next=head[v];     head[v]=alloc++;}void setG(){     CC(E);alloc=0;     memset(head,-1,sizeof(head));      s=0;t=N+1;     int u,v,val;     for( int i=1;i<=N;i++ ){         scanf("%d",&val);         addEdge( s,i,val );         scanf("%d",&val);         addEdge( i,t,val );     }     FF( i,M ){         scanf( "%d%d%d",&u,&v,&val );         addEdge( u,v,val,val );     }}int  sap(){     CC(dis),CC(gap);     FF( i,t+1 ) cur[i]=head[i];     int u=pre[s]=s,maxflow=0,aug=-1;     gap[0]=t+1;     while( dis[s]<=t ){loop:            for( int &i=cur[u];i!=-1;i=E[i].next )            {                 int v=E[i].pos;                 if( E[i].cap&&dis[u]==dis[v]+1 )                 {                     pre[v]=u;                     checkmin( aug,E[i].cap );                     u=v;                     if( v==t )                     {                         maxflow+=aug;                         //printf( "%d\n",maxflow );                         for( u=pre[u];v!=s;v=u,u=pre[u] )                              E[cur[u]].cap-=aug,E[cur[u]^1].cap+=aug;                         aug=-1;                     }                     goto loop;                 }                        }            int mind=t;            for( int i=head[u];i!=-1;i=E[i].next )            {                 int v=E[i].pos;                 if( E[i].cap&&mind>dis[v] )                     cur[u]=i,mind=dis[v];            }            if( --gap[dis[u]]==0 ) break;            gap[dis[u]=mind+1]++;            u=pre[u];     }     return maxflow;}int main(){    while( scanf("%d%d",&N,&M)!=EOF )    {           setG();           printf( "%d\n",sap() );    }    return 0;}