【CodeForces 1152D --- Neko and Aki's Prank】DP

【CodeForces 1152D --- Neko and Aki's Prank】DP

题目来源:点击进入【CodeForces 1152D — Neko and Aki’s Prank】

Description

Neko is playing with his toys on the backyard of Aki’s house. Aki decided to play a prank on him, by secretly putting catnip into Neko’s toys. Unfortunately, he went overboard and put an entire bag of catnip into the toys…

It took Neko an entire day to turn back to normal. Neko reported to Aki that he saw a lot of weird things, including a trie of all correct bracket sequences of length 2n.

The definition of correct bracket sequence is as follows:

  • The empty sequence is a correct bracket sequence,
  • If s is a correct bracket sequence, then (s) is a correct bracket sequence,
  • If s and t are a correct bracket sequence, then st is also a correct bracket sequence.

For example, the strings “(())”, “()()” form a correct bracket sequence, while “)(” and “((” not.

Aki then came up with an interesting problem: What is the size of the maximum matching (the largest set of edges such that there are no two edges with a common vertex) in this trie? Since the answer can be quite large, print it modulo 109+7.

Input

The only line contains a single integer n (1≤n≤1000).

Output

Print exactly one integer — the size of the maximum matching in the trie. Since the answer can be quite large, print it modulo 109+7.

Sample Input

1

Sample Output

1

解题思路

  • 题意:
    给出一个n,然后在匹配树上染色边,每个结点的所有相邻边只能被染色一次。

    问,这颗树上最多染色多少边。

    匹配树,就是深度为2n的树,每个节点都是一个字符串,只包含(,),以长度为2n的合法匹配字符串作为叶子。每个节点的父亲是比自身长度小一的节点。

  • 思路
    如果我们将根节点定义为第0层的话,那么最后一层是2*n层。
    我们可以贪心的从最后一层开始染色,先染色叶子和叶子节点的父亲,并且去除它们,再继续新的染色叶子和叶子节点的父亲。一直持续下去。
    染色的过程中我们可以定义不平衡度,即左括号的数量减右括号的数量。
    长度为x,不平衡度为y的子树染色方案数相同
    例如这些节点((()),()()(,(())(的子树,染色方案数相同
    选择定义dp[x][y],根节点长度为x,不平衡度为y,的子树的染色方案数。
    那么我们可以通过vis[][]数组来标记是否染过色。
    然后将符合题意的染色数量输出即可。

未优化的AC代码:

#include <iostream>
#include <algorithm>
using namespace std;
#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
const int MAXN = 2e3+5;
const int MOD = 1e9+7;
int dp[MAXN][MAXN];
bool vis[MAXN][MAXN];

int main()
{
    SIS;
    int n;
    cin >> n;
    for(int i=2*n-1;i>=0;i--)
    {
        for(int j=0;j<=2*n-i;j++)
        {
            bool flag=false;
            if(j>0) 
            {
                dp[i][j]=dp[i+1][j-1];
                if(!vis[i+1][j-1]) flag=true;
            }
            if(j<2*n-i)
            {
                dp[i][j]=(dp[i][j]+dp[i+1][j+1])%MOD;
                if(!vis[i+1][j+1]) flag=true;
            }
            if(flag) vis[i][j]=true,dp[i][j]=(dp[i][j]+1)%MOD;
        }
    }
    cout << dp[0][0] << endl;
    return 0;
}

在本题中实际上并不需要vis数组,因为我们都知道所有的子树最后的深度是一样的,标记时实际上也是标记一层然后空一层。所以我们只需计算i为奇数时的就行了。当i为奇数时就是我们要染色的那一层。

优化后的AC代码:

#include <iostream>
#include <algorithm>
using namespace std;
#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
const int MAXN = 2e3+5;
const int MOD = 1e9+7;
int dp[MAXN][MAXN];

int main()
{
    SIS;
    int n;
    cin >> n;
    for(int i=2*n-1;i>=0;i--)
    {
        for(int j=0;j<=2*n-i;j++)
        {
            bool flag=false;
            if(j>0) dp[i][j]=dp[i+1][j-1];
            if(j<2*n-i) dp[i][j]=(dp[i][j]+dp[i+1][j+1])%MOD;
            if(i&1) dp[i][j]=(dp[i][j]+1)%MOD;
        }
    }
    cout << dp[0][0] << endl;
    return 0;
}
发布了412 篇原创文章 · 获赞 135 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_41879343/article/details/104103939