您的位置:首页 > 其它

bzoj 2324: [ZJOI2011]营救皮卡丘

2018-02-26 16:25 387 查看

题面

题意

有k个人从0点开始走,他们分开行动,要依此经过所有点,则最短距离是多少.

做法

洛谷 P1251 餐巾计划问题的做法相似.

首先所有点都必须要经过,因此我们可以看做一个人第一次到达i点时,直接到达超级汇点,又从超级源点流出到达i点,这样可以保证每个点都被经过,可以将每个点拆成两个,一个与汇点相连,一个与源点相连,流量均为1,费用为0.

为了保证依次经过,在弗洛伊德时,注意更新时不能用编号较大点更新较小点,两点间距离即为费用.

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define N 360
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;

ll n,m,k,mm

,ans,bb=1,first
,s,t,last
,B
,d
,mf;
bool in
;
struct Bn
{
ll to,next,quan,cst;
} bn[1001000];
queue<ll>que;

inline void add(ll u,ll v,ll w,ll z)
{
bb++;
bn[bb].to=v;
bn[bb].next=first[u];
bn[bb].quan=w;
bn[bb].cst=z;
first[u]=bb;
}

inline void ad(ll u,ll v,ll w,ll z)
{
add(u,v,w,z);
add(v,u,0,-z);
}

inline bool bfs()
{
ll p,q,mn=INF;
for(; !que.empty(); que.pop());
memset(d,0x3f,sizeof(d));
que.push(s);
d[s]=0;
for(; !que.empty();)
{
q=que.front();
que.pop();
in[q]=0;
for(p=first[q]; p!=-1; p=bn[p].next)
{
if(d[bn[p].to]>d[q]+bn[p].cst&&bn[p].quan)
{
d[bn[p].to]=d[q]+bn[p].cst;
last[bn[p].to]=q;
B[bn[p].to]=p;
if(!in[bn[p].to])
{
in[bn[p].to]=1;
que.push(bn[p].to);
}
}
}
}
if(d[t]==INF) return 0;
for(p=t; p!=s; p=last[p])
{
mn=min(mn,bn[B[p]].quan);
}
ans+=mn*d[t];
for(p=t; p!=s; p=last[p])
{
bn[B[p]].quan-=mn;
bn[B[p]^1].quan+=mn;
}
return 1;
}

int main()
{
memset(mm,0x3f,sizeof(mm));
memset(first,-1,sizeof(first));
ll i,j,ii,p,q,o,jj;
cin>>n>>m>>k;
for(i=1; i<=n; i++)mm[i][i]=0;
for(i=1; i<=m; i++)
{
scanf("%lld%lld%lld",&p,&q,&o);
mm[p][q]=mm[q][p]=min(mm[p][q],o);
}
for(ii=0; ii<=n; ii++)
{
for(i=0; i<=n; i++)
{
for(j=0; j<=n; j++)
{
if(ii<=i||ii<=j)
mm[i][j]=mm[j][i]=min(mm[i][j],mm[i][ii]+mm[ii][j]);
}
}
}
for(i=0;i<=n;i++)
{
for(j=i+1;j<=n;j++)
{
if(mm[i][j]!=INF)
{
ad(i,j+n,1,mm[i][j]);
}
}
}
s=n*2+1,t=n*2+2;
ad(s,0,k,0);
for(i=1;i<=n;i++) ad(s,i,1,0),ad(i+n,t,1,0);
for(; bfs(););
cout<<ans;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: