hdu4859 海岸线

4 篇文章 0 订阅
3 篇文章 0 订阅

hdu4859 海岸线

Problem Description
欢迎来到珠海!

由于土地资源越来越紧张,使得许多海滨城市都只能依靠填海来扩展市区以求发展。作为Z市的决策人,在仔细观察了Z市地图之后,你准备通过填充某些海域来扩展Z市的海岸线到最长,来吸引更多的游客前来旅游度假。为了简化问题,假设地图为一个N*M的格子,其中一些是陆地,一些是可以填充的浅海域,一些是不可填充的深海域。这里定义海岸线的长度为一个联通块陆地(可能包含浅海域填充变为的陆地)的边缘长度,两个格子至少有一个公共边,则视为联通。

值得注意的是,这里Z市的陆地区域可以是不联通的,并且整个地图都处在海洋之中,也就是说,Z市是由一些孤岛组成的,比如像,夏威夷?

你的任务是,填充某些浅海域,使得所有岛屿的海岸线之和最长。

Input
输入第一行为T,表示有T组测试数据。
每组数据以两个整数N和M开始,表示地图的规模。接下来的N行,每一行包含一个长度为M的字符串,表示地图,‘.’表示陆地,’E’表示浅海域,’D’表示深海域。

[Technical Specification]

  1. 1 <= T <= 100
  2. 1 <= N, M <= 47

Output
对每组数据,先输出为第几组数据,然后输出最长的海岸线长度。

Sample Input
3
2 2
EE
EE
3 3
EEE
.E.
EEE
3 3
EEE
DED
EEE

Sample Output
Case 1: 8
Case 2: 16
Case 3: 20
Hint
对于第三组样例,一种可行方案是:

.E.
D.D
.E.

这样5个孤立小岛的海岸线总长为4 * 5 = 20。

这道题是bc#1的一道题目,很显然质量灰常高,我还被Claris坑了..

首先这道题要求我们求最长海岸线,由于地图上某一条海岸线是否存在是根据它两端的格子是什么而决定的,我们可以想到这道题是最小割

我们可以先在这个 n×m 的矩阵周围围上一层D,表示边界

对于地图上 n×m 个点,我们可以从源向所有点连一条边表示这个点选择成为陆地所产生的贡献,从这些点连向汇表示这个点选择成为深海所产生的贡献。那么割掉与源相连的边就是选择成为陆地,割掉与汇相连的边就是选择成为深海·

对于相邻的两个点,如果他们选择不同的还要产生多1的贡献,所以把相邻的两个点连一条双向边且流量为1

连完之后我们发现,由于这是最小割,所以得出来的结果是最短海岸线的长度,与题目要求不符。那么怎么改变使得满足题意呢?

我们只需要转化一下,由于题目要求最大,而最小割只能求最小,所以我们可以想到让最小割求最小损耗,再用所有海岸线减去它便可以了。现在给我们的问题就是如何求最小损耗

首先我们要明白怎样才会产生损耗。只有这一条海岸线两边格子相同才会产生损耗,那么也就是说在最小割割边的时候如果某两个相邻的点同时割去选择成为陆地的边(或者同时割去选择成为深海的边)就会产生多1的损耗

但我们发现这个在我们原来的构图是不能实现的,所以我们要改变原来的构图

对于一对相邻的点,其中一个点连源表示选择成为什么另一个点连汇就要表示选择成为同样的。这样我们就可以在其中间连出一条流量为1的双向边满足条件

1

这样构图就能通过跑最小割求出最长海岸线,当然不要忘记有一些海岸线是固定有(一个为D一个为.)或者固定没有的(两个都为D或.),这个要预处理的啊!

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
using namespace std;
const int Maxn = 50;
const int dx[4] = { 0, 1, 0, -1 };
const int dy[4] = { 1, 0, -1, 0 };
struct node {
    int x, y, next, c, opp;
}a[Maxn*Maxn*20]; int first[Maxn*Maxn], len;
int _min ( int x, int y ){ return x < y ? x : y; }
void ins ( int x, int y, int c ){
    len ++; int k1 = len;
    a[len].x = x; a[len].y = y; a[len].c = c;
    a[len].next = first[x]; first[x] = len;
    len ++; int k2 = len;
    a[len].x = y; a[len].y = x; a[len].c = 0;
    a[len].next = first[y]; first[y] = len;
    a[k1].opp = k2;
    a[k2].opp = k1;
}
int st, ed, h[Maxn*Maxn];
int n, m;
char s[Maxn][Maxn];
int getnum ( int x, int y ){ return (x-1)*m+y; }
bool bfs (){
    queue <int> q;
    memset ( h, -1, sizeof (h) ); h[st] = 0;
    q.push (st);
    while ( !q.empty () ){
        int x = q.front (); q.pop ();
        for ( int k = first[x]; k; k = a[k].next ){
            int y = a[k].y;
            if ( h[y] == -1 && a[k].c > 0 ){
                h[y] = h[x]+1;
                q.push (y);
            }
        }
    }
    return h[ed] > 0;
}
int dfs ( int x, int flow ){
    if ( x == ed ) return flow;
    int delta = 0;
    for ( int k = first[x]; k; k = a[k].next ){
        int y = a[k].y;
        if ( h[y] == h[x]+1 && a[k].c > 0 && flow-delta > 0 ){
            int minf = dfs ( y, _min ( a[k].c, flow-delta ) );
            delta += minf;
            a[k].c -= minf;
            a[a[k].opp].c += minf;
        }
    }
    if ( delta == 0 ) h[x] = -1;
    return delta;
}
int main (){
    int i, j, k, T, Ti;
    scanf ( "%d", &T );
    Ti = 0;
    while ( T -- ){
        scanf ( "%d%d", &n, &m );
        len = 0; memset ( first, 0, sizeof (first) );
        for ( i = 1; i <= n; i ++ ) scanf ( "%s", s[i]+1 );
        for ( i = 0; i <= n+1; i ++ ) s[i][0] = s[i][m+1] = 'D';
        for ( i = 0; i <= m+1; i ++ ) s[0][i] = s[n+1][i] = 'D';
        int sum = 0;
        st = 0; ed = n*m+1;
        for ( i = 1; i <= n; i ++ ){
            for ( j = 1; j <= m; j ++ ){
                if ( s[i][j] == 'D' || s[i][j] == '.' ){
                    for ( k = 0; k < 4; k ++ ){
                        int ii = i+dx[k], jj = j+dy[k];
                        if ( s[i][j] == s[ii][jj] ){
                            sum ++;
                            if ( ii == 0 || ii == n+1 || jj == 0 || jj == m+1 ) sum ++;
                        }
                    }
                }
                else {
                    int sl = 0, sh = 0;
                    for ( k = 0; k < 4; k ++ ){
                        int ii = i+dx[k], jj = j+dy[k];
                        if ( s[ii][jj] == 'D' ) sh ++;
                        else if ( s[ii][jj] == '.' ) sl ++;
                        else ins ( getnum (i,j), getnum (ii,jj), 1 );
                    }
                    if ( (i+j) % 2 == 1 ) ins ( st, getnum (i,j), sl ), ins ( getnum (i,j), ed, sh );
                    else ins ( st, getnum (i,j), sh ), ins ( getnum (i,j), ed, sl );
                }
            }
        }
        int ans = 0, delta;
        while ( bfs () ){
            while ( delta = dfs ( st, 0x7fffffff ) ) ans += delta;
        }
        printf ( "Case %d: %d\n", ++Ti, n*(m+1)+(n+1)*m-ans-sum/2 );
    }
    return 0;
}

给一下对拍代码
vio

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int dx[4] = { 0, 1, 0, -1 };
const int dy[4] = { 1, 0, -1, 0 };
int n, m, ans;
struct lnode {
    int x, y;
}list[2100]; int tot;
bool v[50][50];
void check (){
    int i, j, k;
    int sum = 0;
    for ( i = 0; i <= n+1; i ++ ){
        for ( j = 0; j <= m+1; j ++ ){
            for ( k = 0; k < 4; k ++ ){
                int ii = i+dx[k], jj = j+dy[k];
                if ( ii < 0 || ii > n+1 || jj < 0 || jj > m+1 ) continue;
                if ( v[i][j] != v[ii][jj] ) sum ++;
            }
        }
    }
    if ( sum/2 > ans ) ans = sum/2;
}
void dfs ( int x ){
    if ( x == tot+1 ) check ();
    else {
        v[list[x].x][list[x].y] = false;
        dfs (x+1);
        v[list[x].x][list[x].y] = true;
        dfs (x+1);
    }
}
int main (){
    int i, j, k, T, Ti;
    Ti = 0;
    scanf ( "%d", &T );
    while ( T -- ){
        scanf ( "%d%d", &n, &m );
        char c;
        tot = 0;
        for ( i = 1; i <= n; i ++ ){
            getchar ();
            for ( j = 1; j <= m; j ++ ){
                scanf ( "%c", &c );
                if ( c == 'D' ) v[i][j] = false;
                else if ( c == '.' ) v[i][j] = true;
                else {
                    tot ++;
                    list[tot].x = i; list[tot].y = j;
                }
            }
        }
        for ( i = 0; i <= n+1; i ++ ) v[i][0] = v[i][m+1] = false;
        for ( i = 0; i <= m+1; i ++ ) v[0][i] = v[n+1][i] = false;
        ans = 0;
        dfs (1);
        printf ( "Case %d: %d\n", ++Ti, ans );
    }
    return 0;
}

maker

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define db double
using namespace std;
int n;
bool v[1100][1100];
int main()
{
    int x, last, y;
    srand(time(0));
    int t = rand () % 10+1;
    printf ( "%d\n", t );
    while ( t -- ){
    int n = 5, m = 5;
    printf ( "%d %d\n", n, m );
    memset ( v, false, sizeof (v) );
    for ( int i = 1; i <= n; i ++ ){
        for ( int j = 1; j <= m; j ++ ){
            int x = rand () % 3;
            if ( x == 0 ) printf ( "." );
            if ( x == 1 ) printf ( "D" );
            if ( x == 2 ) printf ( "E" );
        }
        printf ( "\n" );
    }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值