2018/4/7 DP练习赛

一直调不对代码
不光是考试的时候
晚上也是
很烦
心态爆炸
跟T3杠了四个钟头
唉。。。

T1

原题戳这里–>HDU 1176(要是我也有这种运气。。。)
这题其实跟数字三角形很像(没错就是那道dp入门题)
用时间t来分层,每一次t++都会对原来的点最左边和最右边分别加一个可能的位置
然后就变成了数字三角形了对吧
所以我们就可以得出状态转移方程:
f[i][j]+=max(f[i-1][j],max(f[i-1][j-1],f[i-1][j+1]))
这里需要注意的是,当j=0或j=10时,不能再往左走或往右走(因为馅饼只能落在0-10的范围内)
题目简单,不贴代码

T2

原题戳这里–>cf 474D
这题可以通过观察得出状态转移方程(其实就是递推式)
就是某道走楼梯的题
只是走楼梯一次可以走一格或两格
这题是一次吃一或k个
所以方程是:
f[i]=f[i-1]+f[i-k]
f[i]是当有i个蛋糕时的方法数
那么怎么求x1-x2的方法数和呢
求一个前缀数组就好啦
由于要取模,所以我在取模的时候只对f数组取了模,为了防止最后减法的时候得到负数
或者可以在取模的时候先加上模数
不贴代码

T3

题目戳这里–>cf 366C
这道题目说起来就气
考试的时候我非常轻松地想到就是0/1背包
然后迅速敲了一个dfs暴搜
然后就跟n<=100的数据杠上了
先想了记忆化
发现并不能过第六组数据
然后发现我的记忆化删多了(因为我记忆化的是a和b的累加和,可能会有suma,sumb重复,而走到这一种情况的方法不同)
然后就开始疯狂想剪枝
然后。。。
不是剪多了WA就是减少了TLE(我*
最后罚时30次。。。
所以正经的方法是:
读入的时候预处理,将每个水果的能量值储存为美味程度减去k倍的能量值
那么在做0/1背包的时候只要输出f[0]的值就行了
接下来有一个问题
f数组的下标没有负的怎么办
所以可以用强大的偏移!!!
对于每个i加上100000就可以啦
对了
当b[i]>=0时
我们需要反着搜
而b[i]<0时
就得正着搜(就是从小往大搜)
贴一下代码

include<bits/stdc++.h>
using namespace std;
int a[101];
int n,k;
int f[400001];
int b[101];
int y;
int main()
{
    scanf("%d%d",&n,&k);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&y);
        b[i]=a[i]-y*k;
    }
    memset(f,-1,sizeof(f));
    f[100000]=0;//由于偏移,f[0]就是现在的f[100000]
    for (int i=1;i<=n;i++)
    {
        if (b[i]>=0)//正搜与反搜
        {
            for (int j=200000;j>=0;j--)
                if (f[j]!=-1 && j+b[i]<=200000)
                    f[j+b[i]]=max(f[j]+a[i],f[j+b[i]]);
        }
        else
        {
            for (int j=0;j<200000;j++)
                if (f[j]!=-1 && j+b[i]>=0)
                    f[j+b[i]]=max(f[j]+a[i],f[j+b[i]]);
        }
    }
    if (f[100000]==0)
        f[100000]=-1;
    cout <<f[100000] <<endl;
    return 0;
}

T4

题目戳这里–>cf 149D
这题的话。。。
状态转移的限制条件有点多
f数组设四维,分别表示左括号的位置,右括号的位置,左括号染色和右括号染色
当l+1==r的时候
f[l][r][0][1]=1;
f[l][r][1][0]=1;
f[l][r][2][0]=1;
f[l][r][0][2]=1;
//0表示不染色,1表示染红色,2表示染蓝色
当l和r是一对的时候
f[l][r][i][j]+=f[l+1][r-1][x][y];
//x!=i y!=j i,j∈[0,2] i!=j i和j必须只有一个为0
当有并列的括号时
f[l][r][i][j]+=f[l][l的配对][i][x]*f[l的配对+1][r][y][j]
//x,y不能同时=1或2
其实思路也还行,只是限制条件太多了。。。
贴代码

include<bits/stdc++.h>
using namespace std;
const long long p=1000000007;
long long f[701][701][3][3]={};
int match[701];
int q[701];
string s;
int tail=0;
void dfs(int l,int r)
{
    if (l+1==r)
    {
        f[l][r][1][0]=1;
        f[l][r][0][1]=1;
        f[l][r][2][0]=1;
        f[l][r][0][2]=1;
    }
    else if (match[l]==r)
    {
            dfs(l+1,r-1);
            for (int x=0;x<=2;x++)
                for (int y=0;y<=2;y++)
                {
                    if (y!=1)
                    {
                        f[l][r][0][1]+=f[l+1][r-1][x][y];
                        f[l][r][0][1]%=p;
                    }
                    if (x!=1)
                    {
                        f[l][r][1][0]+=f[l+1][r-1][x][y];
                        f[l][r][1][0]%=p;
                    }
                    if (y!=2)
                    {
                        f[l][r][0][2]+=f[l+1][r-1][x][y];
                        f[l][r][0][2]%=p;
                    }
                    if (x!=2)
                    {
                        f[l][r][2][0]+=f[l+1][r-1][x][y];
                        f[l][r][2][0]%=p;
                    }
                }
        }
        else
        {
            dfs(l,match[l]);
            dfs(match[l]+1,r);
            for (int i=0;i<=2;i++)
                for (int x=0;x<=2;x++)
                    for (int y=0;y<=2;y++)
                        for (int j=0;j<=2;j++)
                            if (!((x==1&&y==1) || (x==2&&y==2)))
                            {
                                f[l][r][i][j]+=f[l][match[l]][i][x]*f[match[l]+1][r][y][j];
                                f[l][r][i][j]%=p;
                            }
        }
}
void check_()
{
    for (int i=0;i<=s.size()-1;i++)
    {
        if (s[i]=='(')
        {
            q[++tail]=i+1;
        }
        if (s[i]==')')
        {
            match[q[tail]]=i+1;
            tail--;
        }
    }
}
int main()
{
    cin >>s;
    check_();
    dfs(1,s.size());
    long long ans=0;
    for (int i=0;i<=2;i++)
        for (int j=0;j<=2;j++)
        {
            ans+=f[1][s.size()][i][j];
            ans=ans%p;
        }
    cout <<ans <<endl;
    return 0;
}

T5

留白

T6

题目戳这里–>cf 432D
KMP来啦哈哈哈哈哈哈哈
为了让我们深入理解next数组的含义

所以有了这道题
这道题的一半昨天刚刚由lzydalao讲过
就是求一个字符串的子串在整个字符串中出现过几次
KMP的正常套路
然后另一半呢
通过观察我们可以发现(无敌的观察法)(不知道怎么证明)
满足题目条件的子串就是next[next[next[…next[len]]]](就是while循环不停往下直到next[i]为0)
所以while循环可以很容易解决这个问题
其实还是像递推
贴代码

#include<bits/stdc++.h>
using namespace std;
int next[100001];
char s[100001];
int j;
int num[100001];
int a[100001];
int ans[100001];
int k;
int numm=0;
int main()
{
    scanf("%s",s+1);
    k=strlen(s+1);
    j=0;
    next[0]=0;
    for (int i=2;i<=k;i++)
    {
        while (j>0 && s[j+1]!=s[i])
            j=next[j];
        if (s[j+1]==s[i])
            j++;
        next[i]=j;
    }//处理出next数组
    for (int i=k;i>=1;i--)
    {
        ans[i]++;
        ans[next[i]]+=ans[i];
    }//求出每一个前缀出现的次数
    a[++numm]=k;
    while (k!=0)
    {
        a[++numm]=next[k];
        k=next[k];
    }//while循环求出满足条件的前缀存在a中
    cout <<numm-1 <<endl;
    for (int i=numm-1;i>=1;i--)
        cout <<a[i] <<" " <<ans[a[i]] <<endl;
    return 0;
}

这题主要就是看对next数组的理解(超强的next数组)

T7

留白

T8

留白

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值