2018.4.7DP练习赛

前言


今天要DP练(kao)习(shi),历经10h,因为是第一天竞赛课,早上照例睡懒觉,起来已经没早饭吃了,默默的啃着牛肉棒打着比赛。作为DP的蒟蒻,我也是很慌张了。在我还没吃完的时候,已经有人A了两题了,吓得我……虽然好像是**的,啊呀不管了。

T1


hdu1176

看完题目我是一脸懵逼,完全没有思路,是写完T2T3再回来做的。
看到题目当时就想到FJ收苹果,但发现不一样,就开始在草稿纸上瞎画,莫名写出状态。以时间为状态。一下子就变成一道水的dP了
状态转移方程为: d p [ i ] [ j ] = m a x ( d p [ i + 1 ] [ j 1 ] , d p [ i + 1 ] [ j ] , d p [ i + 1 ] [ j 1 ] ) + p i e [ i ] [ j ] 。pie[i][j]为时间i时在j位置掉的馅饼数目。

//By Bibi
///                 .-~~~~~~~~~-._       _.-~~~~~~~~~-.
///             __.'              ~.   .~              `.__
///           .'//                  \./                  \\`.
///        .'//                     |                     \\`.
///       .'// .-~"""""""~~~~-._     |     _,-~~~~"""""""~-. \\`.
///     .'//.-"                 `-.  |  .-'                 "-.\\`.
///   .'//______.============-..   \ | /   ..-============.______\\`.
/// .'______________________________\|/______________________________`.
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define dep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
const int MAXN=100010;
int read(){
    int sum=0,flag=1;
    char c;
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=-1;
    for(;c>='0'&&c<='9';c=getchar())sum=(sum<<1)+(sum<<3)+c-'0';
    return sum*flag;
}
int n;
int f[MAXN][11];
int ans;
int x,y;
int maxx;
int fmax(int a,int b,int c){
    int max1;
    max1=a>b?a:b;
    max1=max1>c? max1:c;
    return max1;
}
void DP(){
    dep(i,maxx-1,0)
    {
        rep(j,1,9) f[i][j]+=fmax(f[i+1][j-1],f[i+1][j],f[i+1][j+1]);
        f[i][0]+=max(f[i+1][0],f[i+1][1]);
        f[i][10]+=max(f[i+1][10],f[i+1][9]);
    }
    printf("%d\n",f[0][5]); 
}
void init(){
    n=read();
    while(n!=0){
        memset(f,0,sizeof f);
        maxx=0;
        rep(i,1,n){
            x=read();y=read();
            f[y][x]++;
            maxx=max(maxx,y);
        } 
        DP();
        n=read();
    }
}
int main(){
    init();
    return 0;
}

T2


codeforces 474D flowers
题意很简单,就相当于走楼梯,一次走一格和一次走k格,求方案数,问区间的方案数和。
和走楼梯差不多,没什么可说的。但是某s神以为k=1时两种走法是一样的,于是特判了一下,疯狂罚时,可以感受到她的mmp了,像我这种考虑不到的蒟蒻反而一遍A了。

//By Bibi
///                 .-~~~~~~~~~-._       _.-~~~~~~~~~-.
///             __.'              ~.   .~              `.__
///           .'//                  \./                  \\`.
///        .'//                     |                     \\`.
///       .'// .-~"""""""~~~~-._     |     _,-~~~~"""""""~-. \\`.
///     .'//.-"                 `-.  |  .-'                 "-.\\`.
///   .'//______.============-..   \ | /   ..-============.______\\`.
/// .'______________________________\|/______________________________`.
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define dep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
const int MAXN=100010;
const int mod=1000000007;
int read(){
    int sum=0,flag=1;
    char c;
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=-1;
    for(;c>='0'&&c<='9';c=getchar())sum=(sum<<1)+(sum<<3)+c-'0';
    return sum*flag;
}
int t,k;
int l[MAXN],r[MAXN];
int ans;
int f[MAXN];
int sum[MAXN];
int maxx;
void DP(){
    memset(f,0,sizeof f);
    memset(sum,0,sizeof sum);
    rep(i,1,k-1) f[i]=1;
    f[k]=2;
    rep(i,k+1,maxx) f[i]=(f[i-1]%mod+f[i-k]%mod)%mod;    
    rep(i,1,maxx) sum[i]=(sum[i-1]%mod+f[i]%mod)%mod;
}
void init(){
    t=read();k=read();
    rep(i,1,t){
        l[i]=read();r[i]=read();
        maxx=max(maxx,r[i]);
    }
    DP();
    rep(i,1,t){
        int ans;
        ans=sum[r[i]]-sum[l[i]-1];
        printf("%d\n",(ans+mod)%mod);
    }
}
int main(){
    init();
    return 0;
}

T3


CodeForces 366C

有n个水果, 每个水果都有两个属性值ai表示美味程度, bi表示能量值, 现在要求选出一个或多个水果, 使得选出的水果的ai和与bi和的比例是k 问在这种清形可能出现的情况下ai的和最多是多少, 如果这样的情形不存在输出 -1

刚开始想的太简单了,求了两个前缀和,因为都是正整数,根据贪心,取的越多越好,然后判断比例,居然还过了5个点,忽然发现不一定是连续取的,后来来问我的z神也是这个错误(别问我为什么考试的时候还讨论)。其实看到题目的已经想到用背包做了,但是不会写啊。现在重新考虑背包的写法。先将ab转化为a-k*b,在线性上处理,但是你会发现有正数有负数,莫y姓大佬用的是偏移量,我懒得写了,就正数负数分开做了两遍背包,但是在做负数的时候要注意,不是01背包,是完全背包,当时卡了好久。

//By Bibi
///                 .-~~~~~~~~~-._       _.-~~~~~~~~~-.
///             __.'              ~.   .~              `.__
///           .'//                  \./                  \\`.
///        .'//                     |                     \\`.
///       .'// .-~"""""""~~~~-._     |     _,-~~~~"""""""~-. \\`.
///     .'//.-"                 `-.  |  .-'                 "-.\\`.
///   .'//______.============-..   \ | /   ..-============.______\\`.
/// .'______________________________\|/______________________________`.
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define dep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
const int MAXN=1e6;
const int INF=1e9;
int read(){
    int sum=0,flag=1;
    char c;
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=-1;
    for(;c>='0'&&c<='9';c=getchar())sum=(sum<<1)+(sum<<3)+c-'0';
    return sum*flag;
}
int n,k;
struct fruit{
    int a,b;
}p[MAXN];
int f[MAXN];
int dp[MAXN];
int dpp[MAXN];
int x[MAXN],xx[MAXN];
int y[MAXN];
int yfu[MAXN];
void init(){
    n=read();k=read();
    rep(i,1,n) p[i].a=read();
    rep(i,1,n) p[i].b=read()*k; 
    rep(i,1,n) f[i]=p[i].a-p[i].b;
}
void DP(){
    int cot1=1;
    int cot2=1;
    int sum1=0;
    int sum2=0;
    int res=0;
    rep(i,1,n){
        if(f[i]<0){
            x[cot1]=p[i].a;
            yfu[cot1]=-f[i];
            ++cot1;
            sum1+=yfu[cot1-1];
        }
        else if(f[i]>0){
            xx[cot2]=p[i].a;
            y[cot2]=f[i];
            ++cot2;
            sum2+=y[cot2-1] ;
        }
        if(f[i]==0) res+=p[i].a;
    }
    cot1--;
    cot2--;
    rep(i,1,sum1) dp[i]=-INF;
    dp[0]=0;
    int c=0;
    rep(i,1,cot1){
        dep(j,sum1,yfu[i]){
            dp[j]=max(dp[j],dp[j-yfu[i]]+x[i]);
        }
    }
    rep(i,1,sum2) dpp[i]=-INF;
    rep(i,1,cot2){
        dep(j,sum2,y[i]){
            dpp[j]=max(dpp[j],dpp[j-y[i]]+xx[i]);
        }
    }
    int ans=0;
    int minn=min(sum1,sum2);
    rep(i,1,minn){
        ans=max(dpp[i]+dp[i],ans);
    }
    ans+=res;
    if(!ans) printf("-1\n");
    else printf("%d\n",ans);
}
int main(){
    init();
    DP();
//  rep(i,1,n) cout<<f[i]<<" ";
//  printf("%d",ans);
    return 0;
}

T4


CodeForces 149D
给一个合法的括号串,然后问这串括号有多少种涂色方案,当然啦!涂色是有限制的。
  1,每个括号只有三种选择:涂红色,涂蓝色,不涂色。
  2,每对括号有且仅有其中一个被涂色。
  3,相邻的括号不能涂相同的颜色,但是相邻的括号可以同时不涂色。

T4当时硬是没肝出来,后来听了大佬的讲述才A掉的

先每对对括号进行记录。
dp的时候,当左右两边括号是匹配的时候,就为两个括号染色,然后就把状态转移到它们里面的括号串了
当不匹配的时候,也就是这个串是由多个匹配串并列而成的,那么就是dfs的思想了,为第一对括号染色,并求这对括号的子括号串的染色方案数,然后dp后面的那些串,直到dp到只剩一个串,并根据乘法原则把各种方案累计起来。(这样做是因为染色是和两边的括号颜色有关系的,而我们在dp前必须确定这个串两端的括号颜色。
递归的停止是当括号只有两个时,返回可能的染色数。

//By Bibi
///                 .-~~~~~~~~~-._       _.-~~~~~~~~~-.
///             __.'              ~.   .~              `.__
///           .'//                  \./                  \\`.
///        .'//                     |                     \\`.
///       .'// .-~"""""""~~~~-._     |     _,-~~~~"""""""~-. \\`.
///     .'//.-"                 `-.  |  .-'                 "-.\\`.
///   .'//______.============-..   \ | /   ..-============.______\\`.
/// .'______________________________\|/______________________________`.
#include<bits/stdc++.h>
typedef long long LL;
const int MAXN=710;
const LL MOD=1e9+7;
char str[MAXN];
int color[MAXN];
int w[4][2]={{1, 0}, {0, 1}, {2, 0}, {0, 2}};
// 0: 无色
// 1: 红
// 2: 蓝
int f[MAXN][MAXN][4][4],match[MAXN];
int stk[MAXN],cnt;

int dp(int l,int r){
    LL sum=0,pre,els;
    if (l==r-1){
        if (color[l - 1] != 1) sum++;
        if (color[l - 1] != 2) sum++;
        if (color[r + 1] != 1) sum++;
        if (color[r + 1] != 2) sum++;
        return sum;
    }
    if (match[l]==r){
        for (int i=0;i<4;++i) {
            if (color[l - 1] == w[i][0] && w[i][0]) continue;
            if (color[r + 1] == w[i][1] && w[i][1]) continue;
            color[l] = w[i][0];
            color[r] = w[i][1];
            if (!f[l + 1][r - 1][w[i][0]][w[i][1]])
                f[l + 1][r - 1][w[i][0]][w[i][1]] = dp(l + 1, r - 1);
            sum += f[l + 1][r - 1][w[i][0]][w[i][1]];
            sum %= MOD;
        }
    } else {
        for (int i = 0; i < 4; i++) {
            if (color[l - 1] == w[i][0] && w[i][0]) continue;
            color[l] = w[i][0];
            color[match[l]] = w[i][1];
            if (l == match[l] - 1)
                pre = 1;
            else {
                if (!f[l + 1][match[l]-1][w[i][0]][w[i][1]])
                    f[l + 1][match[l]-1][w[i][0]][w[i][1]] = dp(l + 1, match[l] - 1);
                pre = f[l + 1][match[l]-1][w[i][0]][w[i][1]];
            }
            if (!f[match[l]+1][r][color[match[l]]][color[r+ 1]])
                f[match[l]+1][r][color[match[l]]][color[r+ 1]] = dp(match[l] + 1, r);
            els=f[match[l]+1][r][color[match[l]]][color[r+ 1]];
            sum+=pre*els;
            sum%=MOD;
        }
    }
    return sum;
}

int main() {
    gets(str + 1);
    int len = strlen(str + 1);
    // 预处理
    for (int i=len;i>=1;i--)
        if (str[i]==')') stk[cnt++] = i;
        else match[i]=stk[--cnt];
    printf("%d\n",dp(1, len));
    return 0;
}

T5


现有一个字符串 s = s1s2… s|s| of length |s|, 由小写字符组成. 现在有 q 次查询, 每次查询给两个整数 li, ri (1 ≤ li ≤ ri ≤ |s|). 每次查询你的程序要给出此字符串的子串 s[li… ri]有多少个回文串.

CodeForces 245H

当时想的思路是先预处理出所有的回文子串,再跑一遍区间DP,但是硬是过不去,也不知道错哪里了。

后面看题解是边做区间边判断回文子串,用容斥原理,加上两边的减去中间的就好了。很难受,一堆人过的题硬是没过去……

T6


CodeForces 432D

给你一个长度为n的长字符串,“完美子串”既是它的前缀也是它的后缀,求“完美子串”的个数且统计这些子串的在长字符串中出现的次数

看到题目我就很后悔了,为什么没有先看到这题,还能骗个高亮。昨天我刚刚讲过一道非常非常一样的kmp题了。就差这题就能AK的某y神(%%%)很后悔昨天没来听课……

对于一个完美子串,一定是next[n]所在的那条链上,标记一下,做一个类似递归的累加就ojbk了

//By Bibi
///                 .-~~~~~~~~~-._       _.-~~~~~~~~~-.
///             __.'              ~.   .~              `.__
///           .'//                  \./                  \\`.
///        .'//                     |                     \\`.
///       .'// .-~"""""""~~~~-._     |     _,-~~~~"""""""~-. \\`.
///     .'//.-"                 `-.  |  .-'                 "-.\\`.
///   .'//______.============-..   \ | /   ..-============.______\\`.
/// .'______________________________\|/______________________________`.
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define dep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
const int MAXN=1e5+7;
int read(){
    int sum=0,flag=1;
    char c;
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=-1;
    for(;c>='0'&&c<='9';c=getchar())sum=(sum<<1)+(sum<<3)+c-'0';
    return sum*flag;
}
char s[MAXN];
int next[MAXN];
int len;
int ans;
int f[MAXN];
int p[MAXN];
struct wanmei{
    int l,c;
}a[MAXN];
int tot;
void init(){
    scanf("%s",s+1);
    len=strlen(s+1);
}
int maxx;
void yuchuli(){
    int j=0;
    next[1]=0;
    rep(i,2,len){
        while(j&&s[j+1]!=s[i]) j=next[j];
        if(s[i]==s[j+1]) ++j;
        next[i]=j;
    }
}
void DP(){
    int j=next[len];
    while(j){
        p[++tot]=j;
        j=next[j];
    }
    dep(i,len,1){
        f[i]++;
        f[next[i]]+=f[i];
    }
}
int main(){
    init();
    yuchuli();
    DP();
    printf("%d\n",tot+1);
    dep(i,tot,1) printf("%d %d\n",p[i],f[p[i]]);
    printf("%d %d",len,f[len]);
    return 0;
}

T7


codeforces 709E

树是一个无环联通图. 现在给你一个 n 个点的树. 树的 重心 的定义为,如果把这个点去掉,剩余的每个联通分量的结点个数不会超过.
给定 n个点的树,现在你可以进行一次移边操作. 移边 指的是删除一条边 (不删除相应的点) 再增加一条边 (不增加新的点) 操作后还这个图还具有树的性质. 现在问,如果可以进行一次移边操作,每个点是否可以成为树的重心。

还没写过树形DP啊啊啊啊,先去写完再来说吧

T8


Codeforces700B Connecting Universitie

给定n个城市,他们之间的连接方式为一棵树 ,由n-1条道路直接连接,这n-1条道路的长度都是相同的
现在有 2k 个不同的城市想与其他城市建立友好城市.
现在你的问题是,如何让这些城市完成匹配,或者叫友好城市配对后,这些友好城市之间的距离累加和最大。

一样

猜你喜欢

转载自blog.csdn.net/bbbblzy/article/details/79847542