您的位置:首页 > 其它

UVALive 3661 Animal Run(平面图最小割,边为节点+最短路)

2014-03-10 14:45 459 查看
题目:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1662

题目大意:给你一个 n*m 的平面图,除了方格的边线外还增加了一个,左上角到右下角,这些线都是双向边。有一群动物从整个图的左上角要跑到右下角去,然后要去拦截他们跑到那里,每条边上的权值是拦截这条边所需的人数。问你拦截他们最少的人数。

解题思路:本来是挺像最小割,可是太慢。这题的构图实在是太巧妙了!其实题目就是从左下边界拦到右上边界的最小权值和问题。把每条边看成一个节点,每个三角形的三条道路两两直接相连,只是就是得到另外一个边和点都是 O(nm)的带权图了。然后把左/下边界的所有点(原图里是边)加到队列里作为超级源跑最短路,跑完后取上/右边界的点的最小值就行了。关于上面三角形这样建边,需要好好想想,这样能做到把一个小方格从左上到右下都封死。另外还有边编号那里也需要动点脑筋,挺烦的,我编的方式也挺烦的。。

        很好的一道建边题啊,不管你想到没有,反正我是没有想到。。。= =

代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

const int INF = 0x0fffffff;
const int MAXN = 1111*1111*3;
const int MAXM = MAXN*2;

int cc;

struct Edge
{
int t,next;
} edge[MAXM<<1];

int tot,head[MAXN];

void add_edge(int s,int t)
{
edge[tot].t = t;
edge[tot].next = head[s];
head[s] = tot++;
}

void init()
{
tot = 0;
memset(head,-1,sizeof(head));
}

int id[3][1111][1111];

int get_id(int kind,int x,int y)
{
return id[kind][x][y];
}

int val[MAXN];
bool done[MAXN];
int dis[MAXN];

struct Node
{
int id,val;
Node(int a,int b)
{
id = a;
val = b;
}
bool operator < (const Node& tmp) const
{
return val > tmp.val;
}
};

void dij(int n,int m)
{
priority_queue<Node> q;
memset(done,0,sizeof(done));
for(int i = 0;i < cc;i++)
dis[i] = INF;

for(int i = 0;i < n-1;i++)
{
int id = get_id(1,i,0);
dis[id] = val[id];
q.push(Node(id,val[id]));
}
for(int j = 0;j < m-1;j++)
{
int id = get_id(0,n-1,j);
dis[id] = val[id];
q.push(Node(id,val[id]));
}
while(!q.empty())
{
Node cur = q.top();
q.pop();
if(done[cur.id]) continue;
done[cur.id] = 1;
for(int i = head[cur.id];i != -1;i = edge[i].next)
{
int t = edge[i].t;
int tmp = dis[cur.id]+val[t];
if(dis[t] > tmp)
{
dis[t] = tmp;
q.push(Node(t,tmp));
}
}
}
}

void add(int n,int m)
{
init();
for(int i = 0;i < n;i++)
for(int j = 0;j < m-1;j++)
{
if(i > 0)
{
add_edge(get_id(0,i,j),get_id(1,i-1,j));
add_edge(get_id(0,i,j),get_id(2,i-1,j));
}
if(i != n-1)
{
add_edge(get_id(0,i,j),get_id(1,i,j+1));
add_edge(get_id(0,i,j),get_id(2,i,j));
}
}
for(int i = 0;i < n-1;i++)
for(int j = 0;j < m;j++)
{
if(j > 0)
{
add_edge(get_id(1,i,j),get_id(0,i,j-1));
add_edge(get_id(1,i,j),get_id(2,i,j-1));
}
if(j != m-1)
{
add_edge(get_id(1,i,j),get_id(0,i+1,j));
add_edge(get_id(1,i,j),get_id(2,i,j));
}
}
for(int i = 0;i < n-1;i++)
for(int j = 0;j < m-1;j++)
{
add_edge(get_id(2,i,j),get_id(0,i+1,j));
add_edge(get_id(2,i,j),get_id(1,i,j));
add_edge(get_id(2,i,j),get_id(0,i,j));
add_edge(get_id(2,i,j),get_id(1,i,j+1));
}
}

int main()
{
int ca = 0;
int n,m;
while(~scanf("%d%d",&n,&m) && (n+m))
{
cc = 0;
for(int i = 0;i < n;i++)
for(int j = 0;j < m-1;j++)
{
id[0][i][j] = cc++;
scanf("%d",&val[get_id(0,i,j)]);
}
for(int i = 0;i < n-1;i++)
for(int j = 0;j < m;j++)
{
id[1][i][j] = cc++;
scanf("%d",&val[get_id(1,i,j)]);
}
for(int i = 0;i < n-1;i++)
for(int j = 0;j < m-1;j++)
{
id[2][i][j] = cc++;
scanf("%d",&val[get_id(2,i,j)]);
}
add(n,m);
dij(n,m);
int ans = INF;
for(int j = 0;j < m-1;j++)
ans = min(ans,dis[get_id(0,0,j)]);
for(int i = 0;i < n-1;i++)
ans = min(ans,dis[get_id(1,i,m-1)]);
printf("Case %d: Minimum = %d\n",++ca,ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: