当前位置: 代码迷 >> 综合 >> POJ 3469 构图最小割+链表SAP
  详细解决方案

POJ 3469 构图最小割+链表SAP

热度:15   发布时间:2024-01-20 20:25:27.0

题目大意:

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

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

如果没有惩罚。

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

源点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;
}