SRM 553 - 500 TwoConvexShapes

探讨了TopCoder上一道关于矩阵涂色的问题,通过特定的凸包形状约束,使用动态规划方法计算所有合法涂色方案的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:

http://community.topcoder.com/stat?c=problem_statement&pm=11492


题目大意:

将一个矩阵涂成黑白两色,现有一些格子已经被指定为黑色,有些格子已经被指定为白色。

问有多少种给未涂色格子涂色的方法,能使得两种颜色所组成的图形都是“凸包”。



算法:

不妨考虑一种简单的凸包形式,即对于每一行,白格子都在左边,黑格子都在右边,且自上到下白格子的数目逐行减少(非增)。

则计算出每一行的白格子至少延伸到哪儿和至多延伸到哪儿,再用很常见的递推DP的方式去做就可以了。

那么相应对称的共有四种这样的凸包形式,要分别计算。

最后要去掉会被重复计算的部分。


代码:

#include <string>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <string>
#include <climits>
#include <cmath>
#include <queue>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;

const int maxn = 60;
long long dp[maxn][maxn], sum[maxn][maxn];
int l[maxn], r[maxn];
const long long MOD = 1000000007LL;

class TwoConvexShapes
{
public:
    long long solve(vector <string> &grid, char ch, bool rev)
    {
        int n = grid.size();
        int m = grid[0].size();
        memset(dp, 0, sizeof(dp));
        memset(sum, 0, sizeof(sum));
        int lim = 0;
        long long ans = 0;
        int maxl = 0, minr = m;
        for (int i = 0; i < n; i ++)
        {
            for (l[i] = m - 1; l[i] >= 0; l[i] --)
            {
                if (grid[i][l[i]] == ch)
                {
                    break;
                }
            }
            for (r[i] = 0; r[i] < m; r[i] ++)
            {
                if (grid[i][r[i]] != ch && grid[i][r[i]] != '?')
                {
                    break;
                }
            }
            if (l[i] != -1)
            {
                lim = i;
            }
            maxl = max(maxl, l[i]);
            minr = min(minr, r[i]);
        }
        for (int i = r[0] - 1; i >= max(0, l[0]); i --)
        {
            dp[0][i] = 1;
        }
        for (int i = 0; i < n; i ++)
        {
            for (int j = r[i] - 1; j >= max(l[i], 0); j --)
            {
                if (i > 0)
                {
                    if (j == r[i] - 1)
                    {
                        dp[i][j] = sum[i - 1][j];
                    }
                    else
                    {
                        dp[i][j] = (dp[i - 1][j] + dp[i][j + 1]) % MOD;
                    }
                }
            }
            for (int j = m - 1; j >= 0; j --)
            {
                sum[i][j] = (dp[i][j] + sum[i][j + 1]) % MOD;
            }
            if (i >= lim)
            {
                ans = (ans + sum[i][max(l[i], 0)]) % MOD;
                if (rev && dp[i][m - 1] && i < n - 1)
                {
                    ans --;
                }
            }
        }
        if (rev)
        {
            ans -= max(minr - maxl, 0);
        }
        return ans;
    }

    int countWays(vector <string> grid)
    {
        int n = grid.size();
        long long ans = solve(grid, 'W', false);
        for (int i = 0; i < n; i ++)
        {
            reverse(grid[i].begin(), grid[i].end());
        }
        ans = (ans + solve(grid, 'W', true)) % MOD;
        ans = (ans + solve(grid, 'B', true)) % MOD;
        for (int i = 0; i < n; i ++)
        {
            reverse(grid[i].begin(), grid[i].end());
        }
        ans = (ans + solve(grid, 'B', false)) % MOD;
        return (int)ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值