hdu 3870 Catch the Theves(对偶图,最短路,最小割)

Catch the Theves

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 65768/32768 K (Java/Others)
Total Submission(s): 1653    Accepted Submission(s): 521


Problem Description
A group of thieves is approaching a museum in the country of zjsxzy,now they are in city A,and the museum is in city B,where keeps many broken legs of zjsxzy.Luckily,GW learned the conspiracy when he is watching stars and told it to zjsxzy.
Zjsxzy decided to caught these thieves,and he let the police to do this,the police try to catch them on their way from A to B. Although the thieves might travel this way by more than one group, zjsxzy's excellent police has already gather the statistics that the cost needed on each road to guard it. 
Now ,zjsxzy's conutry can be described as a N*N matrix A,Aij indicates the city(i,j) have bidirectionals road to city(i+1,j) and city(i,j+1),gurad anyone of them costs Aij.
Now give you the map,help zjsxzy to calculate the minimium cost.We assume thieves may travel in any way,and we will catch all passing thieves on a road if we guard it.
 

Input
The first line is an integer T,followed by T test cases.
In each test case,the first line contains a number N(1<N<=400).
The following N lines,each line is N numbers,the jth number of the ith line is Aij.
The city A is always located on (1,1) and the city B is always located on (n,n).
Of course,the city (i,j) at the last row or last line won't have road to (i,j+1) or (i+1,j).
 

Output
For each case,print a line with a number indicating the minimium cost to arrest all thieves.
 

Sample Input
  
  
1 3 10 5 5 6 6 20 4 7 9
 

Sample Output
  
  
18
Hint
The map is like this:
 

Source
题意:小偷从左上角出发,要到达右下角,问至少拦截多长的路一定可以抓到小偷

其中每一个交点的权值值=以该点为左端点和上端点的边的权值 如图所示

思路:很明显的一个最小割问题,可是问题规模太大,怎么办?

我们考虑简化,可以把原平面图转换成一个对偶图,具体转换方法可以看

《浅析最大最小定理在信息学竞赛中的应用》这篇论文,讲得很清楚了。

所谓的对偶图就是把原图中的面转换成点,原图中的边如果两边是不同的面,那么就对应对偶图里面两个不同点之间的边,如果原图中两边是相同的面,那么对应对偶图里面一条自己到自己的边。

注意此方法只适用于源点和汇点在最外面的情况。如图左上角和右下角,取中间的就不行了。

我们将s-t(源点到汇点)连接一条虚边,就会把最外面的平面分割成两个平面,我们假设里面的是s面,外面的是t面

那么此时建立对偶图,然后去掉从s-t直接相连的边,然后可以发现

现在从s-t的任何一条路径都是原图的一个割,那么此时s-t的最短路就等于求原图的最小割了。

很巧妙的思路..

注意可能C++里面STL跑的比较慢,用库函数的队列只有G++能过

换成手动循环队列就都能过了

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
#define N 170050
#define INF 999999999
int ma[405][405];
struct Edge
{
    int v,next,w;
}edge[N*5];
int cnt,head[N];
int vis[N],d[N];
int que[N];
void init()
{
    cnt=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int w)
{
    edge[cnt].v=v;edge[cnt].w=w;
    edge[cnt].next=head[u];head[u]=cnt++;

    edge[cnt].v=u;edge[cnt].w=w;
    edge[cnt].next=head[v];head[v]=cnt++;
}
int spfa(int s,int t)
{
    memset(vis,0,sizeof(vis));
    for(int i=s;i<=t;i++)
        d[i]=i==s?0:INF;
    vis[s]=1;
    int top=0,tail=0;
    que[tail++]=s;
    while(top!=tail)
    {
        int u=que[top];
        top=(top+1)%N;
        vis[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v,w=edge[i].w;
            if(d[v]>d[u]+w)
            {
                d[v]=d[u]+w;
                if(!vis[v])
                {
                    vis[v]=1;
                    que[tail]=v;
                    tail=(tail+1)%N;
                }
            }
        }
    }
    return d[t];
}
int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        init();
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            scanf("%d",&ma[i][j]);
        int s=0,t=(n-1)*(n-1)+1;
        for(int i=1;i<n;i++)
            {
                addedge(s,(i-1)*(n-1)+1,ma[i][1]);
                addedge(s,(n-2)*(n-1)+i,ma[n][i]);///处理s面
                addedge(t,(i-1)*(n-1)+n-1,ma[i][n]);
                addedge(t,i,ma[1][i]);///处理t面
            }
        for(int i=1;i<n;i++)
        {
            for(int j=1;j<n;j++)
            {
                if(i>1) addedge((i-2)*(n-1)+j,(i-1)*(n-1)+j,ma[i][j]);
                if(j>1) addedge((i-1)*(n-1)+j-1,(i-1)*(n-1)+j,ma[i][j]);
            }
        }
        printf("%d\n",spfa(s,t));
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值