首先可以发现的是,因为两条线段不能在除了端点处相交,因此整个多边形最终一定会被连接成最上方由若干条线段在端点处相交连成,每条线段下方又是一个子结构。

因此你会发现,每个合法的状态都能被分成两个合法的子结构,因此可以考虑使用区间 \(dp\) 来解决这个问题。

首先,我们简单地考虑令 \(dp_{i, j}\) 表示只使用 \(i, j\) 这个区间之间的边将 \(i, j\) 这个区间联通的方案。

  • 直接在 \(i, j\) 之间连边。

那么枚举每个 \(k\) 做为中间的断点,那么就有转移:

\[dp_{i, j} = \sum\limits_{k = i} ^ {j - 1} dp_{i, k} \times dp_{k + 1, j}(a_{i, j} = 1)
\]
  • 不直接在 \(i, j\) 之间连边。

同样考虑枚举中间的端点 \(k\),那么一个合法的方案就会被描述为用 \(i \sim k\) 之间的边联通 \(i \sim k\) 另一边类似,于是有转移:

\[dp_{i, j} = \sum\limits_{k = i + 1} ^ {j - 1} dp_{i, k} \times dp_{k, j}
\]

但其实你会发现,同一个断点的方案会被所有最外层线段交点的位置计算一次,那么一个最简单的想法就是让这个方案只被计算一次。

于此同时你可以发现我们目前只会计算直接连接两个端点的方案,因此让这个方案在左上边第一个线段交点的位置计算一次是一个不赖的选择。

因此我们需要改写一下 \(dp\) 的状态,令 \(dp_{i, j, 0 / 1}\) 分别表示直接连接 \(i, j\) 和不直接连接 \(i, j\) 的方案。

那么这里的转移就应该变为:

\[dp_{i, j, 1} = \sum\limits_{k = i + 1} ^ {j - 1} (dp_{i, k, 0} + dp_{i, k, 1}) \times dp_{k, j, 0}
\]

对应地改写第一条转移方程即可。

复杂度 \(O(n ^ 3)\)。

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int N = 500 + 5;
const int Mod = 1e9 + 7;
int n, a[N][N], f[N][N], dp[N][N][2];
int read() {
char c; int x = 0, f = 1;
c = getchar();
while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int Inc(int a, int b) { return (a += b) >= Mod ? a - Mod : a;}
int Mul(int a, int b) { return 1ll * a * b % Mod;}
int main() {
n = read();
rep(i, 1, n) rep(j, 1, n) a[i][j] = read();
rep(i, 1, n) dp[i][i][1] = f[i][i] = 1;
rep(i, 1, n) dp[i][i + 1][0] = f[i][i + 1] = a[i][i + 1];
rep(len, 3, n) rep(i, 1, n) {
int j = i + len - 1; if(j > n) break;
rep(k, i, j - 1) if(a[i][j]) dp[i][j][0] = Inc(dp[i][j][0], Mul(f[i][k], f[k + 1][j]));
rep(k, i + 1, j - 1) dp[i][j][1] = Inc(dp[i][j][1], Mul(f[i][k], dp[k][j][0]));
f[i][j] = Inc(dp[i][j][0], dp[i][j][1]);
}
printf("%d", f[1][n]);
return 0;
}

值得一提的是,\(dp\) 的转移一定要想办法将这个问题拆解成已经解决的子问题的结构,例如区间 \(dp\) 中就一定要能描述为左右两个合法区间的结合或其他的意义。

CF888F Connecting Vertices的更多相关文章

  1. Connecting Vertices CodeForces - 888F (图论,计数)

    链接 大意: 给定邻接表表示两点是否可以连接, 要求将图连成树, 且边不相交的方案数 n范围比较小, 可以直接区间dp $f[l][r]$表示答案, $g[l][r]$表示区间[l,r]全部连通且l, ...

  2. POJ Minimum Cut

    Minimum Cut Time Limit: 10000MS   Memory Limit: 65536K Total Submissions: 9302   Accepted: 3902 Case ...

  3. POJ 2914 Minimum Cut

    Minimum Cut Time Limit: 10000MS   Memory Limit: 65536K Total Submissions: 9319   Accepted: 3910 Case ...

  4. 论文笔记之:Large Scale Distributed Semi-Supervised Learning Using Streaming Approximation

    Large Scale Distributed Semi-Supervised Learning Using Streaming Approximation Google  2016.10.06 官方 ...

  5. CodeForces 682C Alyona and the Tree (树+dfs)

    Alyona and the Tree 题目链接: http://acm.hust.edu.cn/vjudge/contest/121333#problem/C Description Alyona ...

  6. POJ_2914_Minimum_Cut_(Stoer_Wagner)

    描述 http://poj.org/problem?id=2914 求无向图中最小割. Minimum Cut Time Limit: 10000MS   Memory Limit: 65536K T ...

  7. POJ 2914 Minimum Cut 最小割图论

    Description Given an undirected graph, in which two vertices can be connected by multiple edges, wha ...

  8. Shorten Diameter

    Shorten Diameter Time limit : 2sec / Stack limit : 256MB / Memory limit : 256MB Score : 600 points P ...

  9. 2015 多校联赛 ——HDU5302(构造)

    Connect the Graph Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others ...

随机推荐

  1. 序列化之serialVersionUID

    serialVersionUID作用: 序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性. 序列化ID起着关键的作用,java的序列化机制是通过在运行时判断类的serialVer ...

  2. Java 计算加几个月之后的时间

    Java 计算加几个月之后的时间 public static Date getAfterMonth(String inputDate,int number) { Calendar c = Calend ...

  3. Web前端浏览器默认样式重置(CSS Tools: Reset CSS)

    /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 License: none (public domain) */ html, ...

  4. Java初学者作业——定义英雄类(Hero),英雄类中的属性包括:姓名、攻击力、防御力、生命值和魔法值;方法包括:攻击、介绍。

    返回本章节 返回作业目录 需求说明: 定义英雄类(Hero),英雄类中的属性包括:姓名.攻击力.防御力.生命值和魔法值:方法包括:攻击.介绍. 实现思路: 分析类的属性及其变量类型. 分析类的方法及其 ...

  5. 云南农职《JavaScript交互式网页设计》 综合机试试卷③——实现二级分类菜单

    一.语言和环境 实现语言:HTML,CSS,JavaScript,JQuery. 开发环境:HBuilder. 二.题目(100分): 使用Jquery和JavaScript实现二级分类菜单管理 点击 ...

  6. MongoDB 变更流(Change Stream)介绍

    1. 什么是Change Stream Change Stream 是MongoDB用于实现变更追踪的解决方案,类似于关系数据库的触发器,但原理不完全相同: | | Change Stream | 触 ...

  7. Mysql 性能优化记录

    记录工作中有关mysql性能优化的心得和经验 1. where条件中的字段 尽量建立索引 2. where条件中的查询条件等号左边尽量不做处理 如查询日期相关字段,尽量不使用date_fromat 或 ...

  8. 原型模式(python)

    原型模式也叫克隆模式,通过拷贝自身的属性来创建一个新的对象,基本方法就是调用copy模块下的 (浅拷贝)copy() 和(深拷贝)deepcopy() #!/usr/bin/env python3 # ...

  9. vue 传入后台的数据多了个=

    解决方法: 在前端值参时用{} 在后台接收时用Map 来自为知笔记(Wiz)

  10. vue 从后台获取数据并渲染到页面

    一.在 created中调用methods中的方法 二.在methods中通过vuex异步获取后台数据 三.在computed 中计算属性 四.页面中调用computed中的计算后的属性 来自为知笔记 ...