zoukankan      html  css  js  c++  java
  • AcWing 1069. 凸多边形的划分

    题目传送门

    一、算法思路

    这是一个经典的图形学 问题 — 三角剖分

    因为我们现实中常见的一些 多边形图形 存储到计算机中,需要转存为一个个 像素点

    那么如何存储一个 多边形 最简单的方案就是把它转化为 多个三角形 进行存储

    也就是 三角剖分 问题,不只是 凸多边形,还能解决 凹多边形有孔多边形 等问题

    回归本题,本题是一个给定的 凸多边形三角剖分 的最小费用方案

    很显然一个 凸多边形的剖分方案 并不唯一:

    选定 多边形中 两个点 后,找出 三角形第三个点 的方案有 \(n−2\)

    然后还要分别 **划分** 他的 **左右两块区域**

    闫氏\(DP\)分析法

    状态表示—集合\(f_{l,r}\):当前划分到的多边形的左端点是 \(l\),右端点是 \(r\) 的方案
    状态表示—属性\(f_{l,r}\):方案的费用最小
    状态计算—\(f_{l,r}\):

    \(f_{l,r}=min(f_{l,k}+f_{k,r}+w_l×w_k×w_r)(l<k<r)\)

    区间DP 在状态计算的时候一定要 认真 划分好 边界转移,对于不同题目是不一样的

    然后本题非常的嚣张,直接用样例的 \(5\) 的点告诉我们答案会爆 \(int\)\(long\) \(long\)

    并且没有 取模 要求,那就只能上 高精度 了,要是懒的话,那用一下__int128,这个是yyds

    二、数据范围

    本题因为是三个\(10^9\)的数字相乘为最大值范围:

    \(int :2147483648\),即\(2e10\)

    \(long\) \(long\)\(9223372036854775807\) \(9*e19\)

    三个 \(int\)连乘就是 \(2e10*2e10*2e10=8*e30\)

    \(long\) \(long\) 也是装不下,只能使用高精度(__int128也可以)

    三、朴素做法

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 55;
    const int INF = 0x3f3f3f3f;
    int n;
    int w[N];
    int f[N][N];
    
    int main() {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> w[i];
        //枚举区间长度
        for (int len = 3; len <= n; len++)
            for (int l = 1; l + len - 1 <= n; l++) {//枚举左端点
                int r = l + len - 1;//计算右端点
                f[l][r] = INF;      //预求最小,先设最大
                for (int k = l + 1; k < r; k++)//枚举第三点
                    f[l][r] = min(f[l][r], f[l][k] + f[k][r] + w[l] * w[k] * w[r]);
            }
        //输出
        printf("%d", f[1][n]);
        return 0;
    }
    

    四、int128作法

    #include<bits/stdc++.h>
    
    using namespace std;
    /**
     __int128 就是占用128字节的整数存储类型。由于是二进制,范围就是 (-2^{127}) ~ (2^{127}-1),
     如果使用了 unsigned __int128,则范围变成 (0) ~ (2^{128}),即约39位数!
    
     由于 __int128 仅仅是 (GCC) 编译器内的东西,不在 (C++ 98/03/11/14/17/20) 标准内,
     且仅 (GCC4.6) 以上64位版本支持,很多配套都没有,只有四则运算功能
     所以要自己写输入输出。使用方法与 int long long 无异
     */
    typedef unsigned __int128 LL;
    const LL INF = 1e38;
    
    LL read() {
        LL x = 0, f = 1;
        char ch = getchar();
        while (ch < '0' || ch > '9') {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while (ch >= '0' && ch <= '9') {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    void print(LL x) {
        if (x < 0) putchar('-'), x = -x;
        if (x > 9) print(x / 10);
        putchar(x % 10 + '0');
    }
    
    const int N = 55;
    LL f[N][N];
    LL w[N];
    
    
    int main() {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++) w[i] = read();
        for (int len = 3; len <= n; len++) {
            for (int l = 1; l + len - 1 <= n; l++) {
                int r = l + len - 1;
                f[l][r] = INF;
                for (int k = l + 1; k < r; k++)
                    f[l][r] = min(f[l][r], f[l][k] + f[k][r] + w[l] * w[k] * w[r]);
            }
        }
        print(f[1][n]);
        return 0;
    }
    
    

    五、高精度作法

    #include<bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    const int N = 55;
    int n;              //n个顶点
    int w[N];           //每个点的权重
    vector<int> f[N][N];//高精度存储最后的结果值
    
    //对比两个高精度的大小
    bool cmp(vector<int> A, vector<int> B) {
        if (B.size() == 0) return true;
        if (A.size() != B.size()) return A.size() < B.size();
        for (int i = A.size() - 1; i >= 0; i--)
            if (A[i] != B[i]) return A[i] < B[i];
        return true;
    }
    
    //高精加高精
    vector<int> add(vector<int> A, vector<int> B) {
        if (A.size() < B.size()) return add(B, A);
        vector<int> c;
        int t = 0;
        for (int i = 0; i < A.size(); i++) {
            t += A[i];
            if (i < B.size()) t += B[i];
            c.push_back(t % 10);
            t /= 10;
        }
        while (t) c.push_back(t % 10), t /= 10;
        return c;
    }
    
    //乘法高精*低精
    vector<int> mul(vector<int> a, LL b) {
        vector<int> c;
        LL t = 0;
        for (int i = 0; i < a.size(); i++) {
            t += b * a[i];
            c.push_back(t % 10);
            t /= 10;
        }
        while (t) c.push_back(t % 10), t /= 10;
        return c;
    }
    
    int main() {
        //优化输入
        ios::sync_with_stdio(false);
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> w[i];
    
        //区间DP
        //此处初始状态f[l][l+1] = 0 初始就是空vector,不用额外设置
        for (int len = 3; len <= n; len++) {
            for (int l = 1; l + len - 1 <= n; l++) {
                int r = l + len - 1;
                //初始化初始状态
                for (int k = l + 1; k < r; ++k) {
                    //三个顶点权值相乘
                    vector<int> v = mul(mul({w[l]}, w[k]), w[r]);
                    //DP值相加
                    v = add(add(f[k][r], f[l][k]), v);
                    //是不是可以迁移
                    if (cmp(v, f[l][r])) f[l][r] = v;
                }
            }
        }
        //输出最终答案
        vector<int> res = f[1][n];
        //倒序输出
        for (int i = res.size() - 1; i >= 0; i--) printf("%d", res[i]);
        return 0;
    }
    
    

    六、dfs+int128解法

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef unsigned __int128 LL;
    const LL INF = 1e38;
    const int N = 50 + 10;
    LL a[N];
    LL g[N][N];
    
    LL read() {
        LL x = 0, f = 1;
        char ch = getchar();
        while (ch < '0' || ch > '9') {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while (ch >= '0' && ch <= '9') {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    void print(LL x) {
        if (x < 0) putchar('-'), x = -x;
        if (x > 9) print(x / 10);
        putchar(x % 10 + '0');
    }
    
    
    LL dfs(int i, int j) {
        if (j - i <= 1) return g[i][j] = 0;
        if (g[i][j] != INF) return g[i][j];
        LL sum = a[i] * a[j];
        for (int k = i + 1; k < j; ++k)
            g[i][j] = min(g[i][j], dfs(i, k) + dfs(k, j) + sum * a[k]);
    
        return g[i][j];
    }
    
    int main() {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++) a[i] = read();
        //int128的初始化极值,还是老老实实的用for循环吧
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; ++j)
                g[i][j] = INF;
    
        print(dfs(1, n));
        return 0;
    }
    
    
  • 相关阅读:
    linux下修改MAC地址方法
    自定义VBS脚本(统计在指定文件中搜索字符串出现的次数)
    mysql 1053错误,无法启动的解决方法
    VBS自编写脚本。(实现批量修改文件名且在执行前,备份原有文件夹中的文件)
    在命令提示符下,怎么查看windows开启了哪些服务?
    vbs 读取txt是读取特定的行
    Windows XP SP3中远程桌面实现多用户登陆
    Linux关机命令详解
    VBS 读取文本文件特殊字符前如逗号的值并赋值给变量
    VBS基础篇
  • 原文地址:https://www.cnblogs.com/littlehb/p/15775027.html
Copyright © 2011-2022 走看看