P4001 [BJOI2006]狼抓兔子
传送门
思路:
不少题解都是用网络流来做最小割(网络流是什么),但对于一个不会网络流的蒟蒻来做这题相当困难。
听机房daolao说可以重构图做最短路。然后就baidu将平面图转换成一个对偶图,因为网络流的最小割 = 对偶图的最短路,所以只要在对偶图上跑最短路(从左上角跑到右下角)就行了。
由于堆优化的Dijkstra写炸了,冒着“死亡”的风险码了个Spfa的最短路,开了O2竟然卡进800ms。
AC代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<string> #include<queue> #include<vector> #include<map> #include<stack> #include<deque> #include<set> using namespace std; #define maxn 2000005 #define INF 0x3f3f3f3f int cnt,n,m,w; int head[maxn],vis[maxn],dis[maxn]; queue <int> q; struct hh { int to,nex,dis; }t[maxn<<2]; inline int read() { char kr=0; char ls; for(;ls>'9'||ls<'0';kr=ls,ls=getchar()); int xs=0; for(;ls>='0'&&ls<='9';ls=getchar()) { xs=(xs<<3)+(xs<<1)+ls-48; } if(kr=='-') xs=0-xs; return xs; } inline void clear()//初始化 { cnt=0; memset(vis,false,sizeof(vis)); memset(dis,INF,sizeof(dis)); memset(head,-1,sizeof(head)); } inline void add(int nex,int to,int dis)//前向星建图 { t[cnt]=hh{to,head[nex],dis}; head[nex]=cnt++; t[cnt]=hh{nex,head[to],dis}; head[to]=cnt++; } inline void getmap()//将平面图转换为对偶图 { for(int j=1;j<m;++j)//给每块标号 { w=read(); add(0,j*2,w); } for(int i=2;i<n;++i) { for(int j=1;j<m;++j) { w=read(); add(2*(i-2)*(m-1)+j*2-1,2*(i-1)*(m-1)+j*2,w); } } if(n>=2) for(int j=1;j<m;++j) { w=read(); add(2*(n-2)*(m-1)+j*2-1,2*(n-1)*(m-1)+1,w); } for(int i=1;i<n;++i) { for(int j=1;j<=m;++j) { w=read(); if(j==1) add(2*(n-1)*(m-1)+1,2*(i-1)*(m-1)+1,w); else if(j==m) add(2*i*(m-1),0,w); else add(2*(i-1)*(m-1)+(j-1)*2,2*(i-1)*(m-1)+(j-1)*2+1,w); } } for(int i=1;i<n;++i) { for(int j=1;j<m;++j) { w=read(); add(2*(i-1)*(m-1)+(j-1)*2+1,2*(i-1)*(m-1)+j*2,w); } } } int main() { clear(); n=read();m=read(); if(n==1 || m==1) { if(n>m) swap(n,m); int ans=INF; for(int i=1;i<m;++i) { w=read(); ans=min(ans,w); } if(ans==INF) ans=0;//特判只有全图只有一个点的情况 printf("%d\n",ans); return 0; } getmap(); dis[0]=0;vis[0]=1;//编号为 0 的点为起始点(左上角) q.push(0); while(!q.empty()) { int u=q.front();q.pop(); vis[u]=0; for (int i=head[u];i!= -1;i=t[i].nex) { int v=t[i].to,w=t[i].dis; if (dis[v]>dis[u]+w) { dis[v] = dis[u] + w; if (!vis[v]) { vis[v] = 1; q.push(v); } } } } printf("%d\n",dis[2*(m-1)*(n-1)+1]);//(2*(m-1)*(n-1)+1)号点为终点(右下角) return 0; }
后记:
因为蒟蒻水平有限,AC这题借鉴了两位大佬的bolg:
①LittleRewriter(洛谷题解)
②kafuuchino (机房的CRK大佬)