bzoj1927: [Sdoi2010]星际竞速

费用流建图。

因为从小往大连边,所以不会出现环。

因为所有点都要经过,所以可以考虑它从那个点拓展而来。拆点,从源点向出点连容量1,费用为定位时间的边,向入点连容量为1,费用为0的边,从出边向汇点连容量为1,费用为0的边,对于x-->y,从x的入点向y的出点连容量为1,费用为时间的边。这样可以保证最大流的每个点都使用且最多拓展出一个点。

然后最小费用流(窝太弱,只会SPFA。。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 2000
#define M 40010
#define inf 100000000
using namespace std;
int n,m,l=1,first[N],next[M],to[M],v[M],c[M],cnt,x,y,z,q[N*2],dis[N],fa[N],Ans,fl[N];
bool flag[N];
void link(int x,int y,int V,int C)
{
	to[++l]=y;v[l]=V;c[l]=C; next[l]=first[x];first[x]=l;
	to[++l]=x;v[l]=0;c[l]=-C;next[l]=first[y];first[y]=l;
}
bool SPFA()
{
	int head=0,tail=1;
	memset(fl,0x3f,sizeof fl);
	memset(flag,0,sizeof flag);
	memset(dis,0x3f,sizeof dis);
	q[1]=0;fl[0]=inf;dis[0]=0;
	while (head!=tail)
	{
		head++;if (head==N) head=0;
		x=q[head];flag[x]=0;
		for (int i=first[x];i;i=next[i])
		{
			if (dis[to[i]]>dis[x]+c[i]&&v[i]>0)
			{
				dis[to[i]]=dis[x]+c[i];
				fa[to[i]]=i;
				fl[to[i]]=min(fl[x],v[i]);
				if (!flag[to[i]])
				{
					flag[to[i]]=1;
					tail++;if (tail==N) tail=0;
					q[tail]=to[i];
				}
			}
		}
	}
	return fl[cnt]<inf;
}
void flow()
{
	for (int i=cnt;i;i=to[fa[i]^1])
	{
		v[fa[i]]-=fl[cnt];
		v[fa[i]^1]+=fl[cnt];
		Ans+=c[fa[i]]*fl[cnt];
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	cnt=n+n+1;
	for (int i=1;i<=n;i++)
		scanf("%d",&x),link(0,i+n,1,x);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		if (x>y) swap(x,y);
		link(x,y+n,1,z);
	}
	for (int i=1;i<=n;i++)
		link(0,i,1,0),link(i+n,cnt,1,0);

	while(SPFA()) flow();
	printf("%d\n",Ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值