[NOI2010]海拔 平面图转对偶图 最小割
题解:
首先,我们不难猜到高度只有 $0$ 或 $1$ 两种可能,而且高度为 0 的地区组成一个联通块,高度为 1 的地区组成一个联通块。只有这样,人们所耗费的体力才是最小的。
得出这个结论,题目就成了求平面图的最小割。
由于最大流等于最小割,网络流的做法是显然的,不过数据过大,不加优化是很难通过的。
我们考虑将平面图转对偶图:
我们知道平面图的最小割就等于对偶图的最短路。
本题和 bzoj1002 狼抓兔子最显著的差别就是本题的边都是有向的,而狼抓兔子的边都是无向的。
读者可以自己在草纸上画一画切割方案的 “极限” 情况,即最小割的形状是无规则的,发现对偶图中的每条边的方向恰好是原有向图的边的方向逆时针旋转 90 度。
建完图跑最短路即可。 (spfa 的话最好加一些优化)
Code:
// luogu-judger-enable-o2 #include<vector> #include<deque> #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<string> using namespace std; typedef long long ll; void setIO(string a){ freopen((a+".in").c_str(),"r",stdin),freopen((a+".out").c_str(),"w",stdout); } void end(){ fclose(stdin),fclose(stdout); } const int maxn=1000002; int idx[600][600], n,s,t; int head[maxn],to[maxn],nex[maxn],val[maxn],edges; void add_edge(int u,int v,int c){ nex[++edges]=head[u],head[u]=edges,to[edges]=v,val[edges]=c; } long long d[maxn]; int inq[maxn]; deque<int>Q; long long spfa() { memset(d,0x3f,sizeof(d)); d[s]=0,inq[s]=1;Q.push_back(s); while(!Q.empty()) { int u=Q.front();Q.pop_front();inq[u]=0; for(int v=head[u];v;v=nex[v]) if(d[to[v]]>d[u]+val[v]) { d[to[v]]=d[u]+val[v]; if(!inq[to[v]]) { inq[to[v]]=1; if(Q.empty()||d[Q.front()]>=d[to[v]])Q.push_front(to[v]); else Q.push_back(to[v]); } } } return d[t]; } int main(){ //setIO("arrangement"); scanf("%d",&n); int cnt=1; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) idx[i][j]=++cnt; s=1,t=cnt+23; int cost; //down->up for(int i=1;i<=n;++i) scanf("%d",&cost),add_edge(idx[1][i],t,cost); for(int i=1;i<n;++i) for(int j=1;j<=n;++j) scanf("%d",&cost),add_edge(idx[i+1][j],idx[i][j],cost); for(int i=1;i<=n;++i) scanf("%d",&cost),add_edge(s,idx[n][i],cost); //left->right for(int i=1;i<=n;++i) { scanf("%d",&cost),add_edge(s,idx[i][1],cost); for(int j=1;j<n;++j)scanf("%d",&cost),add_edge(idx[i][j],idx[i][j+1],cost); scanf("%d",&cost),add_edge(idx[i][n],t,cost); } //up->down for(int i=1;i<=n;++i) scanf("%d",&cost); for(int i=1;i<n;++i) for(int j=1;j<=n;++j)scanf("%d",&cost),add_edge(idx[i][j],idx[i+1][j],cost); for(int i=1;i<=n;++i) scanf("%d",&cost); //right->left for(int i=1;i<=n;++i) { scanf("%d",&cost); for(int j=1;j<n;++j) scanf("%d",&cost),add_edge(idx[i][j+1],idx[i][j],cost); scanf("%d",&cost); } printf("%lld",spfa()); //end(); return 0; }