luoguP4553 80人环游世界
题意
这题主要要处理两个限制:
1.最多有\(m\)个人,即最多\(m\)条路径。
2.每个点\(i\)的经过次数是给定的,为\(a_i\)。
其中2.可以用限定上下界的方法解决,1.在建图中讲。
建图:
新建三个点:\(S'\),\(T'\),\(S''\),将每个点\(i\)拆成入点\(i\)和出点\(i'\)。
1.从\(S''\)向每个入点连容量为\([0,inf]\)费用为\(0\)的边。
2.从每个出点向\(T'\)连容量为\([0,inf]\)费用为\(0\)的边。
3.从\(S'\)向\(S''\)连容量为\([0,m]\)费用为\(0\)的边,这样就解决了限制1。
4.从每个点的入点向出点连容量为\([a_i,a_i]\)费用为\(0\)的边。
5.对于能通行的点对\((i,j)(i<j)\),从\(i\)的出点向\(j\)的入点连边。
之后我们跑\(S'->T'\)的有源汇上下界最小费用流即为答案。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxn=210;
const int inf=1e9;
int n,m,cnt_edge=1,tot,S,T;
int head[maxn],dis[maxn],a[maxn],in[maxn],out[maxn];
int cost[maxn][maxn];
bool vis[maxn];
struct Edge{int u,v,down,up,cost;}E[maxn*maxn+maxn*2];
struct edge{int to,nxt,flow,cost;}e[(maxn*maxn+maxn*4)<<1];
inline int read()
{
char c=getchar();int res=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
inline void add(int u,int v,int w,int c)
{
e[++cnt_edge].nxt=head[u];
head[u]=cnt_edge;
e[cnt_edge].to=v;
e[cnt_edge].flow=w;
e[cnt_edge].cost=c;
}
inline void addflow(int u,int v,int w,int c){add(u,v,w,c);add(v,u,0,-c);}
inline bool spfa()
{
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
queue<int>q;
q.push(S);dis[S]=0;vis[S]=1;
while(!q.empty())
{
int x=q.front();q.pop();vis[x]=0;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(dis[y]>dis[x]+e[i].cost&&e[i].flow>0)
{
dis[y]=dis[x]+e[i].cost;
if(!vis[y])q.push(y),vis[y]=1;
}
}
}
return dis[T]!=0x3f3f3f3f;
}
int dfs(int x,int lim)
{
vis[x]=1;
if(lim<=0||x==T)return lim;
int res=lim;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(dis[y]!=dis[x]+e[i].cost||e[i].flow<=0||vis[y])continue;
int tmp=dfs(y,min(res,e[i].flow));
res-=tmp;
e[i].flow-=tmp,e[i^1].flow+=tmp;
if(res<=0)break;
}
return lim-res;
}
inline int Dinic(int st,int ed)
{
S=st,T=ed;
int res=0,cost=0;
while(spfa())
{
int flow=dfs(S,inf);
res+=flow;cost+=flow*dis[T];
}
return cost;
}
int main()
{
//freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
cost[i][j]=read();
E[++tot]=(Edge){2*n+1,2*n+2,0,m,0};//S'=2*n+1,S''=2*n+2,T'=2*n+3.
for(int i=1;i<=n;i++)E[++tot]=(Edge){2*n+2,i,0,inf,0},E[++tot]=(Edge){i+n,2*n+3,0,inf,0};
for(int i=1;i<=n;i++)E[++tot]=(Edge){i,i+n,a[i],a[i]};
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(~cost[i][j])E[++tot]=(Edge){i+n,j,0,inf,cost[i][j]};
E[++tot]=(Edge){2*n+3,2*n+1,0,inf,0};
for(int i=1;i<=tot;i++)addflow(E[i].u,E[i].v,E[i].up-E[i].down,E[i].cost);
for(int i=1;i<=tot;i++)in[E[i].v]+=E[i].down,out[E[i].u]+=E[i].down;
for(int i=1;i<=2*n+3;i++)//st=2*n+4,ed=2*n+5.
if(in[i]>=out[i])addflow(2*n+4,i,in[i]-out[i],0);
else addflow(i,2*n+5,out[i]-in[i],0);
printf("%d",Dinic(2*n+4,2*n+5));
return 0;
}