CCF-CSP 202104-4校门外的树 动态规划满分题解

CCF-CSP 202104-4校门外的树 动态规划满分题解

题目链接:202104-4校门外的树

思路:

  • 该题目类似选点问题,左右两个状态相乘后再求和,最常见的选择问题是背包问题,则考虑动态规划算法
  • 题目要求在障碍物的地方不能种树,即表明等分点不能取有障碍的点;则在我们选取一个障碍物作为区间端点的时候,另外一个障碍物一定不会出现在种树的点上,即两个障碍物构成的两种方案之间没有交集,就可以考虑分别考虑左右两个状态,即枚举障碍物的位置,然后进行求和
  • 在计算每一段区间内的方案数时,考虑区间的长度,然后考虑该长度的约数即可
  • 当我们对约数进行枚举时,需要对重复的情况进行取舍,因此设置一个数组st进行处理
  • 倒序枚举的原因:能在障碍物处种树的约数,已经被后面的使用过,实现代码为st[d]=true;
  • f[i]表示前i个点的选法总和

具体代码:

#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
const int N = 1e3+10,M = 1e5+10,MOD = 1e9+7;
int n;//输入
int a[N];//输入
int f[N];//状态数组
bool st[M];//判断约数是否使用过
vector<int>q[M];//存储约数
int main()
{
    //初始化约数
    for(int i=1;i<M;i++)
    {
        for(int j=2*i;j<M;j+=i)
        {
            q[j].push_back(i);//逆向思维:i是j的约数,则j是i的倍数
        }
    }
    cin>>n;
    for(int i=0;i<n;i++)cin>>a[i];
    f[0]=1;//没有点,全部都不选,只有一种选法
    //枚举所有状态
    for(int i=1;i<n;i++)
    {
        memset(st, 0, sizeof(st));//每一次都要初始化st
        //倒序枚举最后一个区间的左端点
        for(int j=i-1;j>=0;j--)
        {
            //d为区间长度,cnt该区间内的选法种数
            int d=a[i]-a[j],cnt = 0;
            //枚举d的每个约数
            for(int k:q[d])
            {
                //k在之前没有使用过
                if(!st[k])
                {
                    cnt++;
                    st[k]=true;
                }
            }
            st[d]=true;//障碍物点不能种树
            //对每个约数求和
            f[i]=(f[i]+(LL)f[j]*cnt)%MOD;
        }
    }
    //f[n-1]则代表所有的方案
    cout<<f[n-1]<<endl;
    return 0;
}

你可能感兴趣的:(CCF-CSP,动态规划,算法,c++)