[USACO09FEB]Surround the Islands S

7 篇文章 0 订阅
1 篇文章 0 订阅

题目描述

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

题意翻译

【题目描述】

Farmer John 在加勒比海购置了一片地产,准备在由一系列岛屿组成的农场上养奶牛。 出于他的意愿,他要把所有的岛屿都用篱笆围上。
每个岛都是多边形的。每一次,FJ 会给多边形的一个边(即相邻的两个顶点之间)装上篱笆。对于整个岛屿,他会按照顺时针顺序装上篱笆。由于他想要给所有的岛屿都装上篱笆,某些时候,他必须从一个岛屿坐船到另一个岛屿去。
FJ 可以从任何一个顶点开始装篱笆,也可以从任何一个顶点坐船到另一个岛的某个顶点上,从这个顶点开始把该岛屿的篱笆全都装好,然后马上坐船原路返回。保证任意两个顶点间都有航线。在任意两个顶点之间坐船的费用会在一个矩阵中给出。
所有的岛屿由给定的 NN 对顶点 V_1V1​,V_2V2​ 描述(即:给定顶点 V_1V1​ 与 V_2V2​ 相邻)。每个顶点具体属于哪个岛屿不会在输入中给出。所有顶点由 11 到 NN 标号。
在顶点间坐船旅行的费用由一个 N \times NN×N 的矩阵给出。保证两个岛屿间两个方向的旅行费用相等且不会超过 10001000。
请求出 FJ 把篱笆装完所需要的最小花费。

【输入格式】

第 11 行:包含一个整数 NN,含义见题目描述。
第 22 至第 N+1N+1 行:每行包含两个整数 V_1V1​ 和 V_2V2​,表示这两个顶点在同一个岛屿上且相邻。
第 N+2N+2 行至第 2N+12N+1 行:每行包含 NN 个整数,第 i-N-1i−N−1 行的第 jj 个整数表示从 ii 号顶点坐船到第 jj 号顶点的花费。

【输出格式】

一行一个整数,表示 FJ 把篱笆装完所需要的最小花费。

【说明/提示】

对于所有数据,保证:

  • 3 \leq n \leq 5003≤n≤500
  • 1 \leq V_1,V_2 \leq N1≤V1​,V2​≤N
  • 任意两个顶点之间的旅行花费 \leq 1000≤1000

输入输出样例

输入 #1复制

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 

输出 #1复制

30 

说明/提示

1 10 4

xxxxxxx x

xxxxxxxxx xxxx

7 xxxxxxxxxxx 6 xxxxxxx

xxxxxxxxxxx 11 xxxxxxxxxx 5

xxxxxxx

xxx 3 12 xxxxxxx 2

xxxxxxxx

xxxxxxxx

xxxxxxxxx

xxxxxxxxx

xxxxxxxxxx

xxxxxxxxxx

8 xxxxxxxxxx 9

The example describes three islands: {1,7,3,6,10}, {4,5,11} and {2,9,8,12}. The travel costs are provided as a matrix. For example, the travel cost from vertex 1 to 2 is 15.

There is more than one solution. One is: FJ starts from vertex 3 then 7 and stops at 1, travels to 11 followed by 4,5,11. He then returns back to 1, and travels to 12 followed by 2,9,8,12. Finally, he returns back to 1 and continues with 10,6,3,7. The costs are 8 * 2 = 16 for traveling from 1 to 11 and returning back, and 7 * 2 = 14 for traveling from 1 to 12 and back -- a total cost of 30

1.

【分析】

Tarjan缩点,再在所有强连通分量中找一条最小的边作为强连通分量的边,因为还要回来,所以Ans最后要乘二

【Code】

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>
#define min(x,y)(x<y?x:y)
#define re register
#define For(a,b,c) for(register int a = b;a <= c; a++)
#define IL inline 
char xB[1<<15],*xS=xB,*xT=xB;
#define getchar()(xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)

using namespace std;

typedef long long LL;
IL int read() {
	re int ret=0;re bool flag=0;re char c=getchar();
	while((c<'0')|(c>'9')) flag^=!(c^'-'),c=getchar();
	while((c>='0')&(c<='9')) ret=(ret<<3)+(ret<<1)+(c^'0'),c=getchar();
	return flag?-ret:ret;
}
inline void Put(char * s) { puts(s); }
inline void Put(char c) { Put(&c); }
inline void Put(LL x) { char c[25]={0}; sprintf(c,"%lld",x); Put(c); }
inline void Put(int x) { Put(x|0LL); }

const int N = 500 + 10;
const int M = 25 * 1e4;
const int INF = 66666666;

int n,Low[N],Dfn[N],Nxt[M],Fir[N],To[M],Kind;
int Clock,Tot,Ql[N],Dis[N][N],Stack[N],Top,Ans;

IL void Add_edge(int x,int y) { Nxt[++Tot] = Fir[x],To[Tot] = y,Fir[x] = Tot; }

IL void init()   
{
	n = read();
	For(i,1,n)
	{
		int x = read(),y = read();
		Add_edge(x,y);
		Add_edge(y,x);
	}
}

void Tarjan(int Now)
{
	Low[Now] = Dfn[Now] = ++Clock;
	Stack[++Top] = Now;
	for(int i = Fir[Now];i; i = Nxt[i])
	{
		int Go = To[i];
		if(!Dfn[Go])
		{
			Tarjan(Go);
			Low[Now] = min(Low[Now],Low[Go]);
		}
		else 
			if(!Ql[Go])
				Low[Now] = min(Low[Now],Dfn[Go]);
	}
	if(Low[Now] == Dfn[Now])
	{
		Ql[Now] = ++Kind;
		while(Stack[Top] != Now)
		{
			Ql[Stack[Top]] = Kind;
			Top--;
		}
		Top--;
	}
}

IL void solve()
{
	memset(Dis,10,sizeof(Dis));
	
	For(i,1,n)
		if(!Dfn[i])
			Tarjan(i);                        
	For(i,1,n)
		For(j,1,n)
		{
			int Val = read();
			Dis[Ql[i]][Ql[j]] = min(Dis[Ql[i]][Ql[j]],Val);
		}
	int Tmp = 0;
	Ans = INF;
	
	For(i,1,Kind)
	{
		For(j,1,Kind)
		{
			if(i == j) continue;
			Tmp += Dis[i][j];
		}
		Ans = min(Ans,Tmp);
		Tmp = 0;
	}
	Put(Ans<<1);
}

int main(void)
{
	init();
	solve();
	return 0;
}

2.

直接搜索就好了呀

因为顶点相连就是一个岛,直接找一下岛并找一下每个岛之间的最短距离。
找到之后因为每个点都相连,直接双重for就行 因为去了其他岛要回来,就不存在最短路
最后记得*2


上代码

#include<bits/stdc++.h>
using namespace std;
int n,m[501][501],ans;//m是存的每个点的距离 
int num,f[501],ne[1001],to[1001];//邻接表存储 
void add(int a,int b)//邻接表添加 
{
	ne[++num]=f[a];
	f[a]=num;
	to[num]=b;
}
int sa[501][501],s[501],v[501];
//sa为每个岛之间的最短距离,s为每个点属于哪个岛,v是标记这个点有没有被找过 
void dfs(int u)//找岛 
{
	if(v[u])//如果找过 
		return;
	s[u]=num;//标记为某个岛 
	v[u]=1;
	for(int i=f[u];i!=0;i=ne[i])//邻接表查找 
	{
		int w=to[i];
		if(!v[w])//如果没找过 
		{
			dfs(w);
		}
	}
}
int main()
{

scanf("%d",&n);
for(int i=1;i<=n;i++)
{
	int x,y;
	scanf("%d%d",&x,&y);
	add(x,y);
	add(y,x);
}
for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
		scanf("%d",&m[i][j]);
//输入不解释 

num=0;//岛的标号及数量 
for(int i=1;i<=n;i++)
	if(!v[i])//如果没找过 
	{
		num++;
		dfs(i);
	}

for(int i=1;i<=num;i++)
	for(int j=1;j<=num;j++)
		sa[i][j]=999999999;//初始化 

for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)//找每个岛之间的距离 
	if(s[i]!=s[j])//同岛距离为零 
	{
		sa[s[i]][s[j]]=min(sa[s[i]][s[j]],m[i][j]);
	}
	else
		sa[s[i]][s[j]]=0;

ans=99999999;//初始化 
for(int i=1;i<=num;i++)//暴力枚举每个岛 
{
	int ss=0;
	for(int j=1;j<=num;j++)//枚举距离 
		ss+=sa[i][j];
	//同岛距离为零,不影响 
	ans=min(ans,ss);
}
cout<<ans*2;//往返是两次距离 

return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值