【Luogu】P2045方格取数加强版(最小费用最大流)
通过这题我学会了引诱算法的行为,就是你通过适当的状态设计,引诱算法按照你想要它做的去行动,进而达到解题的目的。
最小费用最大流,首先将点拆点,入点和出点连一条费用=-权值,容量=1的边,再连费用=0,容量=INF的边,跑最小费用最大流即可。
#include<cstdio> #include<cctype> #include<algorithm> #include<cstring> #include<cstdlib> #include<queue> #define maxn 10000 #define maxm 500000 using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } int count(int i){ return i&1?i+1:i-1; } int calc(int i,int j,int n){ return (i-1)*n+j; } struct Edge{ int from,next,to,val,flow,dis; }edge[maxm]; int head[maxn],num; inline void addedge(int from,int to,int val,int dis){ edge[++num]=(Edge){from,head[from],to,val,0,dis}; head[from]=num; } inline void add(int from,int to,int val,int dis){ addedge(from,to,val,dis); addedge(to,from,0,-dis); } bool vis[maxn]; int dst[maxn]; int flow[maxn]; int pre[maxn]; int Start,End; int spfa(){ memset(vis,0,sizeof(vis)); memset(dst,127/3,sizeof(dst)); queue<int>q; q.push(Start); dst[Start]=0; flow[Start]=0x7fffffff; while(!q.empty()){ int from=q.front(); q.pop(); vis[from]=0; for(int i=head[from];i;i=edge[i].next){ int to=edge[i].to; if(dst[to]<=dst[from]+edge[i].dis||edge[i].val<=edge[i].flow) continue; pre[to]=i; dst[to]=dst[from]+edge[i].dis; //printf("%d %d %d\n",from,to,dst[to]); flow[to]=min(flow[from],edge[i].val-edge[i].flow); if(vis[to]) continue; vis[to]=1; q.push(to); } } if(dst[End]==dst[End+1]) return 0; int now=End; while(now!=Start){ int ret=pre[now]; edge[ret].flow+=flow[End]; edge[count(ret)].flow-=flow[End]; now=edge[ret].from; } return dst[End]; } int q[maxn][maxn]; int main(){ int n=read(),k=read(); End=maxn-10; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j){ q[i][j]=read(); add(calc(i,j,n),calc(i,j,n)+n*n,1,-q[i][j]); add(calc(i,j,n),calc(i,j,n)+n*n,0x7fffffff,0); } for(int i=1;i<=n;++i) for(int j=1;j<=n;++j){ if(i^n) add(calc(i,j,n)+n*n,calc(i+1,j,n),0x7fffffff,0); if(j^n) add(calc(i,j,n)+n*n,calc(i,j+1,n),0x7fffffff,0); } add(Start,calc(1,1,n),0x7fffffff,0); add(calc(n,n,n)+n*n,End,0x7fffffff,0); int ans=0; for(int i=1;i<=k;++i) ans+=-spfa(); printf("%d",ans); }