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

题目: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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值