题解 UVA1376 【Animal Run】

题解 UVA1376 【Animal Run】

双倍经验题 q w q q q qwqqq qwqqq

如果您 A C AC AC 了本题,您可以去隔壁洛谷P4001狼抓兔子

(完全一样的题目,只是需要更改一下格式)

本题表面上看着像是网络流最小割,但是网络流蒟蒻不会的复杂度可能无法接受

所以本题是一个最短路

所以需要把图转为对偶图

将原图内围成的每一个三角形看作一个虚点

因为要将起点和终点分割开,所以所练的路径异地逆回购是从左或下到达右或上

所以需要 2 2 2 个点,一个点指向左和下的点,一个点被上或右的所有点指向

其余的店指向与之相邻的点

边权为删除该边的代价

所以很建好图跑一遍 d i j k s t r a dijkstra dijkstra 就行了

P . S . P.S. P.S. 可能存在 n n n m = 1 m=1 m=1 的情况,直接输出所有边中的最小值即可

具体细节见代码(蒟蒻写的可能还有一些复杂)

下面附上蒟蒻的代码

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
inline int read()
{
    int r,s=0,c;
    for(;!isdigit(c=getchar());s=c);
    for(r=c^48;isdigit(c=getchar());(r*=10)+=c^48);
    return s^45?r:-r;
}
const int M=2100000,inf=2e9+7;
int n,m,minn,t;
inline int calc(int x,int y,int pos)
{//pos=1下,pos=2 上
    int ans=(2*m-2)*(x-1)+2*y-1;
    if(pos==2)ans++;
    return ans;
}
struct edge
{
    int to,next,val;
}mem[M<<2];
int head[M],cnt;
inline void add(int u,int v,int val)
{
    mem[++cnt].next=head[u];
    mem[cnt].to=v;
    mem[cnt].val=val;
    head[u]=cnt;
}
struct node
{
    int val,num;
    friend bool operator < (node a,node b)
    {
        return a.val>b.val;
    }
}f,ff;
int d[M];
bool vis[M];
inline void dijkstra(int s)
{
    int num=2*n*m-2*n-2*m+4;
    for(int i=1;i<=num;i++)
        d[i]=inf,vis[i]=0;
    priority_queue<node> q;
    f.num=s,f.val=0;
    d[s]=0;
    q.push(f);
    while(!q.empty())
    {
        f=q.top();
        q.pop();
        if(vis[f.num])continue;
        vis[f.num]=1;
        for(int i=head[f.num];i>0;i=mem[i].next)
        {
            int to=mem[i].to;
            if(d[to]>d[f.num]+mem[i].val)
            {
                d[to]=d[f.num]+mem[i].val;
                ff.num=to,ff.val=d[to];
                q.push(ff);
            }
        }
    }
}
int main()
{
    while(1)
    {

        minn=inf; 
        n=read(),m=read();
        if(n==0||m==0)
            return 0;
        memset(mem,0,sizeof(mem));
        memset(head,0,sizeof(head));
        cnt=0;
        int val;
        for(int i=1;i<=n;++i)//横边 
            for(int j=1;j<m;++j)
            {
                val=read();
                minn=min(minn,val);
                if(i==1)
                    add(calc(i,j,2),2*n*m-2*n-2*m+4,val);
                else if(i==n)
                    add(2*n*m-2*n-2*m+3,calc(i-1,j,1),val);
                else
                    add(calc(i-1,j,1),calc(i,j,2),val),add(calc(i,j,2),calc(i-1,j,1),val);
            }
        for(int i=1;i<n;++i)//纵边
            for(int j=1;j<=m;++j)
            {
                val=read();
                minn=min(minn,val);
                if(j==1)
                    add(2*n*m-2*n-2*m+3,calc(i,j,1),val);
                else if(j==m)
                    add(calc(i,j-1,2),2*n*m-2*n-2*m+4,val);
                else
                    add(calc(i,j-1,2),calc(i,j,1),val),add(calc(i,j,1),calc(i,j-1,2),val);
            }
        for(int i=1;i<n;++i)//斜边
            for(int j=1;j<m;++j)
            {
                val=read();
                minn=min(minn,val);
                add(calc(i,j,1),calc(i,j,2),val),add(calc(i,j,2),calc(i,j,1),val);
            }
        if(n==1||m==1)
        {
            printf("%d\n",minn);
            return 0;
        }
        dijkstra(2*n*m-2*n-2*m+3);
        t++;
        printf("Case %d: Minimum = %d\n",t,d[2*n*m-2*n-2*m+4]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值