题解:P1006 [NOIP 2008 提高组] 传纸条
MoCaRabbit · · 题解
可以费用流,但是疑惑的是题解区没有费用流。
我们发现,这要找两条路去传纸条,而每个人有个好心程度且只能传一次。
于是很容易设计费用流,对于每个点流量为
每个边流量为
跑最大费用最大流,设
因为小渊传完后需要回传,所以
并且在费用流中,点,是不能有流量和费用的。
那咋办,我们化点为边。
可以这样,把点拆成一个入点和一个出点。
入点指向出点的边流量和费用就是我们之前讨论的那个点的。
每个那个点的入点都指向它的入边,出边同样。
我们
跑最大费用最大流刚好满足题意。
代码。
//Dinic 算法
#include<bits/stdc++.h>
using namespace std;
// #define int long long
int n,m,s,t;
int head[80010],to[240010],nxt[240010],val[240010],cost[240010],tot=1;
void add(int u,int v,int w,int c){
to[++tot]=v,val[tot]=w,cost[tot]=c;
nxt[tot]=head[u];
head[u]=tot;
}
int maxflow,maxcost,dis[80010],now[80010],cnt[80010];
bool vis[80010];
bool spfa(){
list<int>q;
memset(dis,-0x3f,sizeof dis);
memset(vis,0,sizeof vis);
vis[s]=dis[s]=0,now[s]=head[s];
q.push_back(s);
while(!q.empty()){
int u=q.front();
q.pop_front();
vis[u]=0;
for(int i=head[u];i;i=nxt[i]){
now[to[i]]=head[to[i]];
if(!val[i]) continue;
if(dis[to[i]]<dis[u]+cost[i]){
dis[to[i]]=dis[u]+cost[i];
cnt[to[i]]=cnt[u]+1;
if(vis[to[i]]) continue;
vis[to[i]]=1;
if(dis[to[i]]<dis[q.empty() ? 0 :q.front()]) q.push_front(to[i]);
else q.push_back(to[i]);
}
}
}
memset(vis,0,sizeof vis);
return dis[t]>dis[0]/2;
}
int dinic(int x,int flow){
vis[x]=1;
if(x==t) return flow;
int rest=flow;
for(int i=now[x];i && rest;i=nxt[i]){
now[x]=i;
if(dis[to[i]]!=dis[x]+cost[i] || !val[i] || vis[to[i]]) continue;
int v=dinic(to[i],min(rest,val[i]));
if(!v) dis[to[i]]=dis[0];
val[i]-=v,val[i^1]+=v,maxcost+=cost[i]*v;
rest-=v;
}
return flow-rest;
}
namespace Input{
int a[210][210];
void main() {
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j];
}
}
int get(int x,int y,int k){
return (x-1)*m+y+k*n*m;
}
signed main() {
ios::sync_with_stdio(0);
cin>>n>>m;
s=n*m+1,t=n*m;
Input::main();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
add(get(i,j,0),get(i,j,1),1,Input::a[i][j]),add(get(i,j,1),get(i,j,0),0,-Input::a[i][j]);
if(i<n) add(get(i,j,1),get(i+1,j,0),1,0),add(get(i+1,j,0),get(i,j,1),0,0);
if(j<m) add(get(i,j,1),get(i,j+1,0),1,0),add(get(i,j+1,0),get(i,j,1),0,0);
}
}
int flow=0;
while(spfa()) while(flow=dinic(s,1e18)) maxflow+=flow;
cout<<maxcost;
return 0;
}
此代码能过 ouuan 的大教室中传纸条。