2024-04-06
太空飞行计划问题
最小割模型
源点向实验连边,容量是收益
仪器向汇点连边,容量是花费
割掉一条边,代表放弃实验/购买仪器
合法的情况就是源点汇点不连通,代表要么买了仪器,要么用到这台仪器的所有实验都放弃
记录总收益为 sum,最小割为 res
\(ans=sum-res\)
(这题读入特别恶心)
切糕
没有 \(D\) 的限制的直接建 \(Z+1\) 层
第 \(k\) 层的一个位置 \((i,j)\) 向 \(k+1\) 层的这个位置连 \(\nu(i,j,k)\) 的边
如果割掉代表这个位置在这个点切开
源点向第一层连边,最后一层向汇点连边,便全都为正无穷
目的是切成两半,也就是切到源汇不连通,要求代价最小,直接最小割模板
考虑 \(D\) 的限制
若两个位置 \((x_1,y_1)\) 和 \((x_2,y_2)\) 相邻
我们从 \((x_1,y_1,z)\) 向 \((x_2,y_2,z-D)\) 连一条正无穷的边
这样的话
如果点 \((x,y,z)\) 往上边一层连的边被割掉了,由于还有相邻位置之间的边,源汇还是连通的,想要不连通,就要在相邻的位置在 \(z-D\) 之上割掉某一条边
如果我们在相邻位置割掉的是 \(z+D\) 之上的,那么在 \((x,y)\) 这个位置就还要再割边,这样一定不是最优秀的,所以最小割因为下面那条割掉的边就可以加回来了
这个技巧看起来很有用的样子,不知道之后还会不会用到,先记下来再说
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=66666,M=666666;
const int Inf=1e8;
const int dx[4]={-1,0,0,1};
const int dy[4]={0,-1,1,0};
int X,Y,Z,D;
int s,t;
int hd[N],edg[M],cap[M],nxt[M],idx;
int dep[N],cur[N];
void adde(int u,int v,int w) {
edg[idx]=v,cap[idx]=w,nxt[idx]=hd[u],hd[u]=idx++;
edg[idx]=u,cap[idx]=0,nxt[idx]=hd[v],hd[v]=idx++;
}
bool bfs() {
queue<int> q;
memset(dep,-1,sizeof(dep));
dep[s]=0,cur[s]=hd[s];
q.push(s);
while(!q.empty()) {
int u=q.front();
q.pop();
for(int i=hd[u];~i;i=nxt[i]) {
int v=edg[i];
if(dep[v]==-1&&cap[i]) {
dep[v]=dep[u]+1;
cur[v]=hd[v];
if(v==t) return true;
q.push(v);
}
}
}
return false;
}
int dfs(int u,int lim) {
if(u==t) return lim;
int flw=0;
for(int i=cur[u];~i&&flw<lim;i=nxt[i]) {
cur[u]=i;
int v=edg[i];
if(dep[v]==dep[u]+1&&cap[i]) {
int tmp=dfs(v,min(cap[i],lim-flw));
if(!tmp) dep[v]=-1;
cap[i]-=tmp,cap[i^1]+=tmp;
flw+=tmp;
}
}
return flw;
}
int dinic() {
int res=0,flw;
while(bfs()) while(flw=dfs(s,Inf)) res+=flw;
return res;
}
int getid(int x,int y,int z) {
return ((x-1)*Y+y-1)*Z+z;
}
int main() {
memset(hd,-1,sizeof(hd));
scanf("%d%d%d%d",&X,&Y,&Z,&D);
Z++;
s=0,t=X*Y*Z+1;
for(int k=1;k<Z;k++) {
for(int i=1;i<=X;i++) {
for(int j=1;j<=Y;j++) {
int w;
scanf("%d",&w);
adde(getid(i,j,k),getid(i,j,k+1),w);
}
}
}
for(int i=1;i<=X;i++) {
for(int j=1;j<=Y;j++) {
adde(s,getid(i,j,1),Inf);
adde(getid(i,j,Z),t,Inf);
}
}
for(int x1=1;x1<=X;x1++) {
for(int y1=1;y1<=Y;y1++) {
for(int dr=0;dr<4;dr++) {
int x2=x1+dx[dr],y2=y1+dy[dr];
if(x2<1||x2>X||y2<1||y2>Y) continue;
for(int k=D+1;k<Z;k++)
adde(getid(x1,y1,k),getid(x2,y2,k-D),Inf);
}
}
}
printf("%d\n",dinic());
return 0;
}
aqz and qjy大佬帮我配好了VS Code,%%%,以后不用DEV了
标签:06,04,idx,int,2024,dep,割掉,hd,edg From: https://www.cnblogs.com/Orange-Star/p/18117968