洛谷 P2941 环绕岛屿Surround the Islands 【tarjan缩点】


传送门:洛谷P2941



题目描述
Farmer John has bought property in the Caribbean and is going to try to raise dairy cows on a big farm composed of islands. Set in his ways, he wants to surround all the islands with fence.

Each island in the farm has the shape of a polygon. He fences the islands one side at a time (between a consecutive pair of vertices) and proceeds clockwise around a given island with his fencing

operations. Since he wants to fence all the islands, he must at some point travel to any other islands using a boat.

He can start fencing at any vertex and, at any vertex he encounters, travel to some vertex on another island, fence all the way around it, and then IMMEDIATELY return back to the same vertex on the original island using the same path he traveled before. Each boat trip has a cost defined by a supplied matrix.

The islands are described by a set of N (3 <= N <= 500) pairs of vertices V1,V2 (1 <= V1 <= N; 1 <= V2 <= N) although you must figure out how to assemble them into islands. The vertices are conveniently numbered 1…N.

The cost of traveling by boat between each pair of vertices is given by a symmetric cost matrix whose elements fall in the range 0…1000.

What is the minimum cost of surrounding the islands with the fence?

约翰在加勒比海买下地产,准备在这里的若干个岛屿上养奶牛.所以,他要给所有岛屿围上篱笆.每个岛屿都是多边形.他沿着岛屿的一条边界朝一个方向走,有时候坐船到另一个岛去.他可以从任意一个多边形顶点开始修篱笆的工作;在任意一个到达的顶点,他可以坐船去另一个岛屿的某个顶点,但修完那个岛的篱笆,他必须马上原路返回这个出发的岛屿顶点.任意两个顶点间都有航线,每条航线都需要一定的费用.请帮约翰计算最少的费用,让他修完所有篱笆.

输入格式

  • Line 1: A single integer: N

  • Lines 2…N+1: Each line describes an island’s border with two space-separated integers: V1 and V2

  • Lines N+2…2*N+1: Line i-N-1 contains N integers that describe row i of the cost matrix: Row_i

输出格式

  • Line 1: A single integer that specifies the minimum cost of building the fence

输入输出样例
输入
12
1 7
7 3
3 6
6 10
10 1
2 12
2 9
8 9
8 12
11 5
5 4
11 4
0 15 9 20 25 8 10 13 17 8 8 7
15 0 12 12 10 10 8 15 15 8 8 9
9 12 0 25 20 18 16 14 13 7 12 12
20 12 25 0 8 13 14 15 15 10 10 10
25 10 20 8 0 16 20 18 17 18 9 11
8 10 18 13 16 0 10 9 11 10 8 12
10 8 16 14 20 10 0 18 20 6 16 15
13 15 14 15 18 9 18 0 5 12 12 13
17 15 13 15 17 11 20 5 0 22 8 10
8 8 7 10 18 10 6 12 22 0 11 12
8 8 12 10 9 8 16 12 8 11 0 9
7 9 12 10 11 12 15 13 10 12 9 0

输出
30



题解:
利用tarjan缩点,将每个强连通分量变成单个点,然后计算这些点的最短路(可暴力求,因为数据量不大)。因为题目要求修完每个岛都要原路返回,所以最后求得的ans要乘2。


AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<stack>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N=1100;
int n;
int mp[N][N],dis[N][N];
int dfn[N],low[N],head[N];
int vis[N],belong[N];
int bcnt,k,idx;
stack<int> s;
vector<int> g[N];
struct edge
{
  int to,next;
}e[2*N];
void init()
{
  for(int i=0;i<N;i++) g[i].clear();
  memset(head,-1,sizeof(head));
  memset(vis,0,sizeof(vis));
  memset(belong,0,sizeof(belong));
  memset(dfn,0,sizeof(dfn));
  memset(low,0,sizeof(low));
  memset(dis,inf,sizeof(dis));
  bcnt=0,k=1,idx=0;
}
void add(int u,int v)
{
  e[k].to=v;
  e[k].next=head[u];
  head[u]=k++;
}
void tarjan(int u)
{
  int v;
  dfn[u]=low[u]=++idx;
  s.push(u);
  vis[u]=1;
  for(int i=head[u];i!=-1;i=e[i].next)
  {
    v=e[i].to;
    if(!dfn[v])
    {
      tarjan(v);
      low[u]=min(low[u],low[v]);
    }
    else if(vis[v])
    {
      low[u]=min(low[u],dfn[v]);
    }
  }
  if(dfn[u]==low[u])
  {
    bcnt++;
    do
    {
      v=s.top();
      s.pop();
      vis[v]=0;
      belong[v]=bcnt;
      g[bcnt].push_back(v);
    }while(u!=v);
  }
}
int main()
{
  int a,b;
  init();
  scanf("%d",&n);
  for(int i=0;i<n;i++)
  {
    scanf("%d%d",&a,&b);
    add(a,b);
    add(b,a);
  }
  for(int i=1;i<=n;i++)
  {
    if(!dfn[i])
    {
      tarjan(i);
    }
  }
  int ans=inf;
  for(int i=1;i<=n;i++)
  {
    for(int j=1;j<=n;j++)
    {
      scanf("%d",&mp[i][j]);
      dis[belong[i]][belong[j]]=min(mp[i][j],dis[belong[i]][belong[j]]);
    }
  }
  for(int i=1;i<=bcnt;i++)
  {
    int tmp=0;
    for(int j=1;j<=bcnt;j++)
    {
      if(i==j) continue;
      tmp+=dis[i][j];
    }
    ans=min(ans,tmp);
  }
  printf("%d\n",ans*2);
  return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值