1069. 凸多边形的划分(思维,区间dp)

限制条件

  • 分为N-2个三角形
  • 互不相交 

分析

由于一个三角形是需要三个顶点,我们可以先枚举三个顶点,然后观察一下有什么性质

如图,当我们枚举出i,j,k三个顶点的时候,当前可以划分为三个集合

  • 左边的多边形集合+右边的多边形集合+当前的三角形

这样子看就和能量项链很像了,通过枚举三个点,然后计算集合,由于需要先计算出所有的单个三角形的集合  然后  再递推到一整个多边形中的权值(枚举两个端点,至少中间间隔一个点,所以len为3,才能保证中间间隔至少一个点

所以len至多从3开始枚举,由于有n个顶点,至多n边形,所以枚举到n,3<=len<=n

我们发现,由于不同的三角形之间不能相交,那么对于一个多边形集合来说

状态表示:所有由(i,i+1),(i+1,i+2)...(j-1,j)构成的多边形划分的三角形的集合

子集划分/状态计算:当前dp[i][j]可以划分为三个子集=左边的多边形集合+当前三角形+右边多边形集合,dp[i][j]=dp[i][k]+dp[k][j]+w[i]*w[i]*w[k],根据中断点k的不同可以划分为多个子集

  • 选择i+1作为中断点
  • ...
  • 选择j-1作为中断点

递推顺序:3<=len<=n

初始化:无穷大

边界:dp[i][i]=dp[i][i+1]=0

由于需要高精度,所以这里用vector使用高精度

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 55;
vector<int> dp[N][N];
int w[N];
typedef long long ll;
vector<int> init(ll a)
{
    if (a == (long long)1e18)
    {
        vector<int> res(35, 0);
        res.push_back(1);
        reverse(res.begin(), res.end());
        return res;
    }
    vector<int> res;
    while (a)
    {
        res.push_back(a % 10);
        a /= 10;
    }
    reverse(res.begin(), res.end());
    return res;
}
vector<int> add(vector<int> a, vector<int> b)
{
    vector<int> c;
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    int t = 0;
    for (int i = 0;i < a.size() || i < b.size();i++)
    {
        if (i < a.size())
            t += a[i];
        if (i < b.size())
            t += b[i];
        c.push_back(t % 10);
        t /= 10;
    }
    if (t)
        c.push_back(t);
    reverse(c.begin(), c.end());
    return c;
}
vector<int> mul(vector<int> a, ll r)
{
    vector<int> c;
    reverse(a.begin(), a.end());
    ll t = 0;
    for (int i = 0;i < a.size() || t;i++)
    {
        if (i < a.size())
            t += a[i] * r;
        c.push_back(t % 10);
        t /= 10;
    }
    while (c.back() == 0 && c.size() > 1) c.pop_back();
    reverse(c.begin(), c.end());
    return c;
}
bool cmp(vector<int>& a, vector<int>& b)
{
    if (a.size() != b.size())
        return a.size() < b.size();
    else
        for (int i = 0;i < a.size();i++)
            if (a[i] != b[i])
                return a[i] < b[i];
    return false;
}
int main()
{
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++)
        cin >> w[i];
    for (int i = 1;i <= n;i++)
        for (int j = 1;j <= n;j++)
            dp[i][j] = init((long long)1e18);

    for (int len = 1;len <= n;len++)
        for (int i = 1;i + len - 1 <= n;i++)
        {
            int j = i + len - 1;
            if (len == 1 || len == 2) dp[i][j] = init(0);
            else
                for (int k = i + 1;k < j;k++)
                {
                    auto temp = add(dp[i][k], dp[k][j]);
                    auto t = mul(mul(init(w[i]), w[j]), w[k]);
                    temp = add(temp, t);
                    
                    if (cmp(temp,dp[i][j]))
                        dp[i][j] = temp;
                }
        }
    for (auto t : dp[1][n])
        cout << t;
    return 0;
}

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值