ACM: 图论题 poj 1161 (把图重建成…

本文介绍了一个图论问题的解决方案,该问题旨在寻找一个最优的聚会区域,使得参与者穿越城墙的总数最少。文章详细阐述了解题步骤,包括如何将问题转化为区域间的最短路径问题,构建图模型,以及使用Floyd算法求解。
                                           Walls

 

Description

In a country, great walls have been built in such a way that every great wall connects exactly two towns. The great walls do not cross each other. Thus, the country is divided into such regions that to move from one region to another, it is necessary to go through a town or cross a great wall. For any two towns A and B, there is at most one great wall with one end in A and the other in B, and further, it is possible to go from A to B by always walking in a town or along a great wall. The input format implies additional restrictions.

There is a club whose members live in the towns. In each town, there is only one member or there are no members at all. The members want to meet in one of the regions (outside of any town). The members travel riding their bicycles. They do not want to enter any towns, because of the traffic, and they want to cross as few great walls as possible, as it is a lot of trouble. To go to the meeting region, each member needs to cross a number (possibly 0) of great walls. They want to find such an optimal region that the sum of these numbers (crossing-sum, for short) is minimized.
ACM: <wbr>图论题 <wbr>poj <wbr>1161 <wbr>(把图重建成环为节点)

The towns are labeled with integers from 1 to N, where N is the number of towns. In Figure 1, the labeled nodes represent the towns and the lines connecting the nodes represent the great walls. Suppose that there are three members, who live in towns 3, 6, and 9. Then, an optimal meeting region and respective routes for members are shown in Figure 2. The crossing-sum is 2: the member from town 9 has to cross the great wall between towns 2 and 4, and the member from town 6 has to cross the great wall between towns 4 and 7.

You are to write a program which, given the towns, the regions, and the club member home towns, computes the optimal region(s) and the minimal crossing-sum.

Input

Your program is to read from standard input. The first line contains one integer: the number of regions M, 2 <= M <= 200. The second line contains one integer: the number of towns N, 3 <= N <= 250. The third line contains one integer: the number of club members L, 1 <= L <= 30, L <= N. The fourth line contains L distinct integers in increasing order: the labels of the towns where the members live.

After that the input contains 2M lines so that there is a pair of lines for each region: the first two of the 2M lines describe the first region, the following two the second and so on. Of the pair, the first line shows the number of towns I on the border of that region. The second line of the pair contains I integers: the labels of these I towns in some order in which they can be passed when making a trip clockwise along the border of the region, with the following exception. The last region is the "outside region" surrounding all towns and other regions, and for it the order of the labels corresponds to a trip in counterclockwise direction. The order of the regions gives an integer labeling to the regions: the first region has label 1, the second has label 2, and so on. Note that the input includes all regions formed by the towns and great walls, including the "outside region".

Output

Your program is to write to standard output. The first line contains one integer: the minimal crossing-sum.

Sample Input

10
10
3
3 6 9
3
1 2 3
3
1 3 7
4
2 4 7 3
3
4 6 7
3
4 8 6
3
6 8 7
3
4 5 8
4
7 8 10 9
3
5 10 8
7
7 9 10 5 4 2 1

Sample Output

2

 

题意: 每个城市用城墙围起来, 现在有perNum个人想聚在一起, 地点是在城市和城墙围起来的区域内,

      并且要求全部人穿过城墙数目最少,而且不通过城市. 即: 选择一块区域让他们穿过最少的城墙相聚.

 

解题思路: (一个下午AC的题目)

         1. 一开始, 把图画出来了, 每个区域编号, 现在时求区域之间的最短路.

         2. 把图转化成区域想连接的. (建图成为难点.)

         3. 判断两个区域是否相连, 两个区域是否有至少一条边是共用的. (这个用邻接表还是好办的)

            接着就是用floyd求每个点之间的最少穿过的城墙数.

         4. 图建好, 接着是要求出perNum每个人在什么区域内.

            方法: 枚举每个区域判断人是否在城市上. 是的话, 就有一条边人连接这个区域. (为5做铺垫)

         5. 接着是求最小和.枚举每个区域求最小的每个人到达的距离(穿过的边数)和.

 

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 305
const int INF = (1<<29);

struct node1
{
 int v,next;
}edges[MAX*MAX];

struct node2
{
 int u,num;
}region[MAX];

int n, m;
int dist[MAX][MAX]; //计算最短路径(cross walls number);
int first[MAX*2];
int num;
int person[MAX], perNum; //人的信息

inline int min(int a,int b)
{
 return a < b ? a : b;
}

inline void addedges(int re,int v) //点的相邻区域
{
 edges[num].v = v;
 edges[num].next = first[re];
 first[re] = num++;
}

inline void addregion(int re,int v) //添加区域的边界
{
 edges[num].v = v;
 edges[num].next = region[re].u;
 region[re].u = num++;
}

bool find(int u,int v,int re) //判断边是否在区域re上
{
 int e, j;
 for(e = region[re].u; edges[e].next != -1; e = edges[e].next)
 {
  if( ( u == edges[e].v && v == edges[edges[e].next].v )
   || (u == edges[edges[e].next].v && v == edges[e].v) ) //判断u->v这条边是否在re区域上
   return true;
 }
 j = region[re].u;
 if( (u == edges[j].v && v == edges[e].v)
  || (u == edges[e].v && v == edges[j].v) )
  return true;
 return false;
}

bool judge(int re1,int re2) //判断区域re1,re2是否相邻
{
 int e, j;
 for(e = region[re1].u; edges[e].next != -1; e = edges[e].next)
 {
  int u = edges[e].v;
  int v = edges[edges[e].next].v;
  if( find(u,v,re2) ) //判断区域每条边是否与re2相邻
   return true;
 }

 j = region[re1].u;
 if( find(edges[e].v, edges[j].v, re2) )
  return true;
 return false;
}

void read_graph() //创建图, 以区域为节点的图
{
 num = 0;
 int i, j;
 int v;
 for(i = 0; i < m; ++i)
 {
  region[i].u = -1;
  scanf("%d",&region[i].num);
  for(j = 0; j < region[i].num; ++j)
  {
   scanf("%d",&v);
   addregion(i,v); //添加节点到区域内
  }
 }

 for(i = 0; i < m; ++i)
  for(j = 0; j < m; ++j)
   dist[i][j] = (i == j ? 0 : INF);
 for(i = 0; i< m; ++i)
 {
  for(j = 0; j < m; ++j)
  {
   if( judge(i,j) ) //判断区域i,j是否相邻
    dist[i][j] = dist[j][i] = 1;
  }
  dist[i][i] = 0;
 }
}

void floyd() //求最短路(点对点)
{
 for(int k = 0; k < m; ++k)
 {
  for(int i = 0; i < m; ++i)
  {
   for(int j = 0; j < m; ++j)
    dist[i][j] = min(dist[i][j],dist[i][k] + dist[k][j]);
  }
 }
}

int main()
{
 int i, j, k;
// freopen("input.txt","r",stdin);
 while(scanf("%d",&m) != EOF)
 {
  scanf("%d",&n);
  scanf("%d",&perNum);
  for(i = 0; i < perNum; ++i)
   scanf("%d",&person[i]);
  read_graph();
  floyd();

  memset(first,-1,sizeof(first));
  for(i = 0; i < perNum; ++i)
  {
   for(j = 0; j < m; ++j)
   {
    int e;
    for(e = region[j].u; e != -1 && edges[e].v != person[i]; e = edges[e].next);
    if(e != -1) addedges(i,j);
   }
  }

  int result = INF;
  int sum, temp;
  for(i = 0; i < m; ++i)
  {
   sum = 0;
   for(j = 0;j < perNum; ++j)
   {
    temp = result;
    for(k = first[j]; k != -1; k = edges[k].next)
    {
     temp = min(temp,dist[edges[k].v][i]); //edges[k].v指向的是区域编号
    }
    sum += temp;
   }
   result = min(result,sum);
  }

  printf("%d\n",result);
 }
 return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值