题面:传送门
这道题比较具有迷惑性,题目中特意说明答案要四舍五入保留到整数,其实只要细心观察就能发现最优解一定是左上角一堆‘ ’,右下角一堆‘ ’,那么答案就为‘ ’和‘ ’的分界线上的所有边权和,求一遍最小割就行了。
可是,我们需要注意到,点数可能会达到 ,直接跑最大流算法肯定不能在时限内通过。
参照网上大佬们的题解,我第一次知道还有对偶图这种玩意儿,只要把一个平面图转化成其对应的对偶图,那么此时原图的最小割等于对偶图的最短路长度,这就可以在时限内通过了。
对偶图和它所对应的平面图的区别就是把面当做点,面的交界处当作边,原图中方向相反的边对偶图中也方向相反,对偶图中对应的边权等于原图中的边权,总体代码还是很好实现的,加上 和堆优化,时间复杂度: 。
全部代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
int n;
vector<pair<int,int> >G[250010];
bool vis[250010];
int dis[250010];
priority_queue<pair<int,int> ,vector<pair<int,int> > ,greater<pair<int,int> > >q;
int gtpos(int x,int y)
{
return (x-1)*n+y;
}
void addedge(int x,int y,int w)
{
G[x].push_back(make_pair(y,w));
}
void dijkstra()
{
for(int i=1;i<=n*n+1;i++)dis[i]=2e9;
q.push(make_pair(0,0));
for(int i=0;i=n*n+1;i++)
{
while(!q.empty() && (vis[q.top().second] || dis[q.top().second]!=q.top().first))q.pop();
int x=q.top().second;
q.pop();
vis[x]=true;
if(x==n*n+1)return;
for(int j=0;j<G[x].size();j++)
{
int y=G[x][j].first,w=G[x][j].second;
if(vis[y])continue;
if(dis[x]+w<dis[y])
{
dis[y]=dis[x]+w;
q.push(make_pair(dis[y],y));
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
int x;
scanf("%d",&x);
if(!i)addedge(gtpos(i+1,j),n*n+1,x);
else if(i==n)addedge(0,gtpos(i,j),x);
else addedge(gtpos(i+1,j),gtpos(i,j),x);
}
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
int x;
scanf("%d",&x);
if(!j)addedge(0,gtpos(i,j+1),x);
else if(j==n)addedge(gtpos(i,j),n*n+1,x);
else addedge(gtpos(i,j),gtpos(i,j+1),x);
}
}
for(int i=0;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
int x;
scanf("%d",&x);
if(!i)addedge(n*n+1,gtpos(i+1,j),x);
else if(i==n)addedge(gtpos(i,j),0,x);
else addedge(gtpos(i,j),gtpos(i+1,j),x);
}
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
int x;
scanf("%d",&x);
if(!j)addedge(gtpos(i,j+1),0,x);
else if(j==n)addedge(n*n+1,gtpos(i,j),x);
else addedge(gtpos(i,j+1),gtpos(i,j),x);
}
}
dijkstra();
printf("%d",dis[n*n+1]);
return 0;
}