POJ 1161 Walls(建图+Floyd)

题目大意:

有N个村庄和M个区域,每个区域都是由村庄之间的围墙所围成(每个区域用村庄来描述,按顺时针方向给出区域周围的村庄)。有一个俱乐部的一些成员居住在村庄里,每个村庄最多居住一个成员。现在这些成员要到一个区域去开会,俱乐部成员走到该区域可能会穿墙而过,要求选定一个区域,要求这些成员的总共穿墙次数最小。

解题思路:

一个最小距离问题,关键是如何建图。可以将区域作为点,如果两个区域相邻,那么相应的点之间的权值为1(表示从区域到另一个区域,从一个点到另一个点)要穿过一堵墙。如何确定两个区域是相邻的,因为给出区域的描述的是顺时针方向上的村庄,其中一个区域的一条边(u1, v1),另一个区域的一条边(u2, v2),(都是顺时针给出的),如果u1 = v2 且 u2 = v1 那么这两个区域公用一堵墙,即两个区域相邻。还要处理第一个点和最后一个点的关系。最后一个问题,如果计算从村庄到区域经过的围墙的个数,这需要转化,将村庄到区域所经过的围墙的个数转化为区域(与村庄相邻的区域)到区域所经过的围墙的个数。选取一个最小的即为村庄到区域的信息。

代码:

#include <cstdio>
#include <cstring>
#include <vector>
#define M 300
#define INF 0x3f3f3f3f
using namespace std;

int m, ans, tmp, l;
int member[M];
vector <int> adj[M];    //保存与村庄相邻的区域
vector <int> border[M]; //保存每个区域周围的村庄的信息(顺时针保存)
int dis[M][M];          //从一个区域到另一个区域经过的围墙数目

void floyd() {          //Floyd算法,求任意两点之间的距离
    int i, j, k;
    for(k=0; k<m; k++) {
        for(i=0; i<m; i++) {
            for(j=0; j<m; j++) {
                if(dis[i][k] + dis[k][j] < dis[i][j])
                    dis[i][j] = dis[i][k] + dis[k][j];
            }
        }
    }
}

int main() {
    int  n;
    int i, j, k;
    while(~scanf("%d", &m)) {
        scanf("%d%d", &n, &l);
        for(i=0; i<l; i++) {
            scanf("%d", &member[i]);
            member[i] --;
        }
        for(i=0; i<n; i++) adj[i].clear();
        for(i=0; i<m; i++) border[i].clear();
        for(i=0; i<m; i++) {
            for(j=0; j<m; j++)
                if(i == j) dis[i][j] = 0;
                else
                    dis[i][j] = INF;
        }
        int u, v, su;
        for(i=0; i<m; i++) {
            scanf("%d", &k);
            scanf("%d", &su);
            su --;
            border[i].push_back(su);
            adj[su].push_back(i);
            u = su;
            for(j=1; j<k; j++) {
                scanf("%d", &v);
                v --;
                border[i].push_back(v);
                adj[v].push_back(i);
                for(int ii=0; ii<i; ii++) {
                    for(int jj=0; jj<border[ii].size()-1; jj++)
                        if(border[ii][jj] == v && border[ii][jj+1] == u ) { //注意点的顺序
                            dis[ii][i] = dis[i][ii] = 1;
                        }
                    if(border[ii][0] == u && border[ii][border[ii].size()-1] == v) {  //还是要注意点的顺序
                        dis[ii][i] = dis[i][ii] = 1;
                    }
                }
                u = v;
            }
            v = su;
            for(int ii=0; ii<i; ii++) {
               for(int jj=0; jj<border[ii].size()-1; jj++)
                        if(border[ii][jj] == v && border[ii][jj+1] == u ) {
                            dis[ii][i] = dis[i][ii] = 1;
                        }
                if(border[ii][0] ==u && border[ii][border[ii].size()-1] == v) {
                    dis[ii][i] = dis[i][ii] = 1;
                }
            }
        }
        
        floyd();
        
        ans = INF; tmp = INF;
        int th;
        int tmpAns = 0;
        for(i=0; i<m; i++) {
            tmpAns = 0;
            for(j=0; j<l; j++) {
                tmp = INF;
                int jj = member[j];
                for(k=0; k<adj[jj].size(); k++) {  //寻找与村庄相邻的最优的区域
                    if(dis[i][adj[jj][k]] < tmp) {
                        tmp = dis[i][adj[jj][k]];
                        th = adj[jj][k];
                    }
                }
                tmpAns += dis[i][th];
            }
            if(tmpAns < ans) ans = tmpAns;
        }
        printf("%d\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值