您的位置:首页 > 其它

SOJ 3106: Dual Core CPU

2012-12-09 16:43 288 查看
最小割,建图方法有点儿意思

题目链接:http://cstest.scu.edu.cn/soj/problem.action?id=3106

题意:有若干任务,可以在CPU的A核或B核上运行,花费分别为Wa,Wb。

对于一些有关联的任务,若这两个任务不在同一个核上运行,则需要消耗额外花费cost(a,b)。

求最小花费。

算法:

对于每个任务分别向源点和汇点建边,权值为Wa和Wb。

若两个任务有关联,则在两个点间建双向边,权值为cost(a,b)。

这样对每个点来说,要不然就要割与源点相连的边,要不就要割与汇点相连的边。

并且,若两个点间之间存在边,且两个点中一个割的是与汇点相连的边,另一个割的是与源点相连的边,则这两个点之间的那条边也必须是割边。

否则S->u->v->T构成增广路

我还是第一次碰见既向源点建边又像汇点建边的模型,孤陋寡闻了。。。


一开始还很2B的拆点了。。后来发现拆了之后全连的是双向边,所以拆不拆一样的= =

#include<stdio.h>
#include<string.h>
#define INF 0x7f7f7f7f
int p[51000],d[51000],cur[51000],gap[51000],head[51000];
int E,S,T;

typedef struct
{
int u,v,weigh,next;
}EDGE;

EDGE edge[1100000];
void addedge(int u,int v,int weigh)
{
edge[E].v=v;
edge[E].u=u;
edge[E].weigh=weigh;
edge[E].next=head[u];
head[u]=E++;
edge[E].v=u;
edge[E].u=v;
edge[E].weigh=0;
edge[E].next=head[v];
head[v]=E++;
}

int sap(int n)
{
int v,u,i,a,mind,ans;
for(u=1;u<=n;u++)
{
cur[u]=head[u];
d[u]=gap[u]=0;
}
gap[0]=n;
u=S;ans=0;
memset(p,-1,sizeof(p));
while(d[S]<n)
{
for(i=cur[u];i!=-1;i=edge[i].next)
if(edge[i].weigh&&(d[u]==d[edge[i].v]+1)) break;
if(i!=-1)
{
cur[u]=i;
int v=edge[i].v;
p[v]=i;
u=v;
if(v==T)
{
int a=INF;
for(;i!=-1;i=p[edge[i].u])a=a<edge[i].weigh?a:edge[i].weigh;
for(v=T;v!=S;v=edge[p[v]].u)
{
edge[p[v]].weigh-=a;
edge[p[v]^1].weigh+=a;
}
ans+=a;
u=S;
}
}
else
{
int mind=n;
for(i=head[u];i!=-1;i=edge[i].next)
if(edge[i].weigh)if(mind>d[edge[i].v]+1){mind=d[edge[i].v]+1;cur[u]=i;}
--gap[d[u]];
if(gap[d[u]]==0)return ans;
++gap[mind];
d[u]= mind;
if(u!=S) u=edge[p[u]].u;
}
}
return ans;
}

int main()
{
int cas,n,m,i,ai,bi,val,u,v;
while(scanf("%d%d",&n,&m)==2)
{

E=0;S=1;T=n+2;
memset(head,-1,sizeof(head));
for(i=1;i<=n;i++)
{
scanf("%d%d",&ai,&bi);
addedge(1,i+1,ai);
addedge(i+1,n+2,bi);
}
while(m--)
{
scanf("%d%d%d",&u,&v,&val);
addedge(u+1,v+1,val);
addedge(v+1,u+1,val);
}
printf("%d\n",sap(2*n+2));
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: