题目:80人环游地球
题解
首先根据题意我们要找到题目的约束条件。每个城市只能经过一次,这可以用网络流的最大流来限制。可以知道,此题是用最小费用流来解决的。建模方式:
- 首先,建立一个源点和汇点,根据每个城市只能被经过一次这个条件,将每个城市拆成两个点,分别于源点和汇点连 容量为城市所要经过人数 的边。
- 然后,每个城市之间有花费,连接(i+N,j)。容量为inf,有花费(建边未说明花费则为0)。
- 因为每个城市可以作为一条路径的起点,所以再建一个超级源点ss ,连接(ss ,i),容量为inf。
- 最后跑一个最小费用流。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define _(d) while (d (((ch=getchar())>47)&&ch<58))
using namespace std;
const int Maxn=105;
const int MaxN=225;
const int Maxe=Maxn*Maxn+5000;
const int inf=1000000229;
int N,M,tt,ans,dist[MaxN],pre[MaxN],ct[Maxn][Maxn],ft[Maxn];//cost of every road and number of people of every city
bool vst[MaxN];
int S,T,ss,from[Maxe],to[Maxe],r[Maxe],cost[Maxe],nxt[Maxe],h[MaxN];
inline int Get()
{
char ch;int x;bool f=1;
_(!)if(ch=='-')f=0;x=ch-48;_()x=(x<<3)+(x<<1)+ch-48;
return f?x:-x;
}
inline void addedge(int u,int v,int w,int c)
{
from[tt]=u;to[tt]=v;r[tt]=w;cost[tt]=c;
nxt[tt]=h[u];h[u]=tt++;
}
inline void add(int u,int v,int w,int c)
{
addedge(u,v,w,c);addedge(v,u,0,-c);
}
inline void build()
{
add(S,ss,M,0);
for(int i=1;i<=N;i++)
{
add(ss,i,inf,0);
add(S,i+N,ft[i],0);
add(i,T,ft[i],0);
}
for(int i=1;i<N;i++)
for(int j=i+1;j<=N;j++)
if(ct[i][j]!=-1) add(i+N,j,inf,ct[i][j]);
}
queue<int> q;
inline bool spfa()
{
for(int i=0;i<MaxN;i++) dist[i]=inf,vst[i]=0,pre[i]=0;
int u,v;
dist[S]=0;
vst[S]=1;
q.push(S);
while(!q.empty())
{
u=q.front();
q.pop();
vst[u]=0;
for(int i=h[u];i!=-1;i=nxt[i])
{
v=to[i];
if(r[i]&&dist[u]+cost[i]<dist[v])
{
dist[v]=dist[u]+cost[i];//loosen
pre[v]=i;//mark its the pre edge
if(!vst[v])//make it in queue
{
vst[v]=1;
q.push(v);
}
}
}
}
return dist[T]==inf?0:1;
}
inline void getans()
{
ans=0;
while(spfa())
{
int nowflow=inf;
for(int i=T;i!=S;i=from[pre[i]]) nowflow=min(nowflow,r[pre[i]]);
for(int i=T;i!=S;i=from[pre[i]])
{
int k=pre[i];
ans+=nowflow*cost[k];
r[k]-=nowflow;
r[k^1]+=nowflow;
}
}
}
int main()
{
//freopen("1754.in","r",stdin);
//freopen("1754.out","w",stdout);
for(int i=0;i<MaxN;i++) h[i]=-1;
N=Get();M=Get();
ss=(T=2*N+1)+1;//init
for(int i=1;i<=N;i++)ft[i]=Get();
for(int i=1;i<N;i++)
for(int j=i+1;j<=N;j++)
ct[i][j]=Get();
build();
getans();
printf("%d\n",ans);
return 0;
}