最近做的DP类题的总结~

感觉自己DP确实太弱。。。

Codeforces Round #156 (Div. 1) A. Almost Arithmetical Progression

题意:在保持循序的情况下,从一个数组取出最多的数,这些数是交替出现的,例如,1,2,1,2,、。。

dp[i][j] 表示前i 个数,第 j 个数和第 i 个数被取出可以组成最长序列

我们用pre[i]记录 i 上一次出现的位置,这样就可以好好转移了。。。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long long
#define mod 1000000007
#define maxn 1000010
#define pi acos(-1.0)
#define eps 1e-6
using namespace std;

int dp[4001][4001],a[4010],pre[maxn];
int num[maxn] ;

int main()
{
    int i,m,n,k,j ;
    int id,ans;
    while(scanf("%d",&n) != EOF)
    {
        for( i = 1 ; i <= n ;i++)
            scanf("%d",&a[i]) ;
        memset(pre,-1,sizeof(pre)) ;
        memset(num,0,sizeof(num)) ;
        ans=1;
        for(i = 1 ; i <= n ;i++)
        {
            id=pre[a[i]];
            num[a[i]]++;
            ans=max(num[a[i]],ans);
            pre[a[i]] = i ;
            for( j = 1 ; j < i ;j++)
            {
                if(id==-1)dp[j][i]=2 ;
                else if(j>id)dp[j][i] = 1+dp[id][j];
                else if(j<id)dp[j][i] = dp[j][id];
                ans=max(ans,dp[j][i]) ;
            }
        }
        cout <<ans<<endl;
    }
    return 0 ;
}
/256/problem/A

poj 1141 Brackets Sequence

题意: 给你一个字符串,你可以任意插入字符,使得塔变成回文串,长度要求最小,

然后把这个回文串输出来

dp[i][j]表示把 i-j变成回文串后的最短长度;

pre[x][y][2] 表示x,y从哪里转移过来的

然后 从 1,n经行 dfs,把需要增加字符和它匹配的位置标记

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long long
#define mod 1000000007
#define maxn 210
#define pi acos(-1.0)
#define eps 1e-6
#define INF 0x3f3f3f3f
using namespace std;

int dp[maxn][maxn],pre[maxn][maxn][2] ;
char a[maxn];
bool vi[maxn] ;
bool check(char a,char b)
{
    if(a==‘(‘&&b==‘)‘) return true;
    if(a==‘[‘&&b==‘]‘) return true;
    return false;
}
void out(char a)
{
    if(a==‘(‘||a==‘)‘)printf("()") ;
    else printf("[]");
}
void dfs(int x,int y )
{
    if(x>y) return ;
    if(x>=y){vi[x]=true ;return;}
    int xx = pre[x][y][0],yy=pre[x][y][1] ;
   // cout << xx<<"--" <<yy<<endl;
   // cout<<x<<" "<<y<<endl;
    if(xx==x&&yy+1==y)
    {
        vi[y] = true;
        dfs(xx,yy) ;
    }
    else if(xx-1==x&&yy==y)
    {
        vi[x] = true;
        dfs(xx,yy);
    }
    else if(xx-1==x&&yy+1==y)
    {
        if(yy==xx)vi[xx]=1;
        dfs(xx,yy);
    }
    else
    {
        dfs(xx,yy) ;
        dfs(yy+1,y) ;
    }
}
int main()
{
    int cnt1,cnt2,n,i;
    int j , k , T,m ;
    while(gets(a+1) > 0 )
    {
        n = strlen(a+1) ;
        memset(dp,0,sizeof(dp));
        memset(vi,0,sizeof(vi));
        for( i = 1 ; i <= n ;i++)
        {
            dp[i][i]=2;
            for( j =i-1 ; j >= 1 ;j--)
            {
                dp[j][i] = dp[j][i-1]+2 ;
                pre[j][i][0]=j;
                pre[j][i][1]=i-1;
                if(dp[j+1][i]+2 < dp[j][i]){
                    dp[j][i] = dp[j+1][i]+2;
                    pre[j][i][0]=j+1;
                    pre[j][i][1]=i;
                }
                for( k = j+1 ; k < i ;k++)
                {
                    if(dp[j][k]+dp[k+1][i] < dp[j][i])
                    {
                        dp[j][i] = dp[j][k]+dp[k+1][i] ;
                        pre[j][i][0]=j;
                        pre[j][i][1]=k;
                    }
                }
                if(check(a[j],a[i])&& 2+dp[j+1][i-1] < dp[j][i])
                {
                    dp[j][i] = dp[j+1][i-1]+2;
                    pre[j][i][0]=j+1;
                    pre[j][i][1]=i-1;
                }
            }
        }
        dfs(1,n);
        for( i = 1 ; i <= n ;i++)
        {
            if(vi[i])out(a[i]) ;
            else printf("%c",a[i]);
        }
        puts("") ;
    }
    return 0 ;
}

poj 3280 Cheapest Palindrome

题意:每个字符都有删除它的代价增加它的代价,问最小的代价使得给出的字符串变成回文串

dp[i][j]表示,i-j变成字符串的最小代价

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long long
#define mod 1000000007
#define maxn 2010
#define pi acos(-1.0)
#define eps 1e-6
using namespace std;

int dp[maxn][maxn];
char a[maxn] ;
int cost1[210] ,cost2[210];
int main()
{
    int cnt1,cnt2,n,i;
    int j , k , T,m ;
    char s[5] ;
    while(scanf("%d%d",&n,&m) != EOF)
    {
         scanf("%s",a+1) ;
         for( i = 1 ; i <= n ;i++)
         {
             scanf("%s",s) ;
             scanf("%d%d",&cost1[s[0]],&cost2[s[0]]) ;
         }
         memset(dp,0x3f3f3f,sizeof(dp)) ;
         for( i = 1 ; i <= m;i++)
         {
             dp[i][i]=0;
             for( j = i-1 ; j >= 1 ;j--)
             {
                 dp[j][i] = dp[j][i-1]+min(cost1[a[i]],cost2[a[i]]) ;
                 dp[j][i] = min(dp[j][i],dp[j+1][i]+min(cost1[a[j]],cost2[a[j]])) ;
               //  cout <<j <<" " << i << " "<<dp[j][i] << endl;
                 if(a[i]==a[j])
                 {
                     dp[j][i] = min(dp[j][i],dp[j+1][i-1]) ;
                     if(i==j+1)dp[j][i]=0;
                 }
             }
         }
         cout <<dp[1][m] << endl;
    }
    return 0 ;
}

poj 2955 Brackets

题意:给出一个字符串,按先后循序不变选出一些字符组成一个回文串,要求这个回文串最长

dp[i][j]表示i-j可以选出最长的回文串

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long long
#define mod 1000000007
#define maxn 110
#define pi acos(-1.0)
#define eps 1e-6
using namespace std;

int dp[maxn][maxn];
char a[maxn] ;
bool check(char a,char b)
{
    if(a==‘(‘&&b==‘)‘) return true;
    if(a==‘[‘&&b==‘]‘) return true;
    return false;
}
int main()
{
    int cnt1,cnt2,n,i;
    int j , k , T ;
    while(scanf("%s",a+1) != EOF)
    {
        if(a[1]==‘e‘) break ;
        n = strlen(a+1) ;
        memset(dp,0,sizeof(dp)) ;
        for( j = 1 ; j <= n ;j++)
        {
            for( i = j-1; i >= 1 ;i--)
            {
                dp[i][j] = 0;
                if(check(a[i],a[j]))
                    dp[i][j] = max(dp[i][j],dp[i+1][j-1]+1) ;
                for(k = i ; k < j ; k++)
                    dp[i][j] = max(dp[i][k]+dp[k+1][j],dp[i][j]);
            }
        }
        cout << dp[1][n]*2 << endl;
    }
    return 0 ;
}

hdu 2476 String painter

题意:每个你可以把a的一段居间刷成任意的一个字符,求把a变成 b 需要的最少刷的次数

dp[i][j] 表示把空串(相当于任意串)变成 b 中 i-j 子串的最小刷次数,

dp[i][j] = 1+dp[i+1][j] ;

如果 b[k]==b[i] ( k > i && k <= j ) 那么 b[i] 和 b[k] 可以一起刷出来 ,

dp[i][j] = min(dp[i][j],dp[i+1][k]+dp[k+1][j]) ;

然后 a和b 的匹配, ans[i]表示把 1-i变成相同最小的改变次数

如果 a[i]==b[i] 那么ans[i]=ans[i-1];

不然,就去枚举 1-i

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long long
#define mod 1000000007
#define maxn 210
#define pi acos(-1.0)
#define eps 1e-6
#define INF 0x3f3f3f3f
using namespace std;

int dp[maxn][maxn],ans[maxn] ;
char a[maxn],b[maxn] ;
int main()
{
    int cnt1,cnt2,n,i;
    int j , k , T,m ;
    while(scanf("%s%s",a+1,b+1) != EOF)
    {
         n = strlen(a+1) ;
         memset(dp,0,sizeof(dp)) ;
         for( i = 1 ; i <= n ;i++)
         {
             dp[i][i]=1;
             for( j = i-1 ; j >= 1 ;j--)
             {
                 dp[j][i] = 1+dp[j+1][i];
                 for( k = j+1 ; k <= i ;k++)if(b[j]==b[k])
                     dp[j][i] = min(dp[j][i],dp[j+1][k]+dp[k+1][i]) ;
                  //   cout << j <<" " <<i<<" "<<dp[j][i]<<endl;
             }
         }
         ans[0]=0;
         for( i= 1 ; i <= n ;i++)
         {
             if(a[i]==b[i])ans[i]=ans[i-1] ;
             else
             {
                 ans[i]=ans[i-1]+1;
                 for( j = i ; j >= 1 ;j--)
                    ans[i]=min(ans[i],ans[j-1]+dp[j][i]) ;
             }
         }
         cout <<ans[n]<<endl;
    }
    return 0 ;
}

poj 3661 Running

题意:给出某个人n 分钟内每分钟可以跑步的距离,跑一分钟疲劳增加1 ,疲劳度不能超过 m  ,

他某个时候可以选择休息,休息一分钟可以疲劳度减1,不过等到疲劳度为 0 才可以继续跑步

在 第n 分钟结束时,他的疲劳度必须为 0 ,问最长可以跑多远

dp[i][j] 表示,第i 分钟,疲劳度为 j 时最远可以跑多远

如果选择跑步 dp[i][j] = dp[i-1][j-1]+a[i]

不过 dp[i-1][j]不能转移到它,因为必须休息到0才能跑,

如果转移了,那么下一次用到它的转移就会出错。

dp[i][0] = dp[i-1][0] ;

或者第 j 分钟 选择休息 dp[i][0] = max(dp[i][0],dp[j][i-j]) ;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<map>
#include<ctime>
#include<bitset>
#define LL long long
#define mod 1000000007
#define maxn 10010
#define pi acos(-1.0)
#define eps 1e-6
#define INF 0x3f3f3f3f
using namespace std;

int dp[10001][510];
int a[maxn];
int main()
{
    int cnt1,cnt2,n,i;
    int j , k , T,m ;
    while(scanf("%d%d",&n,&m) != EOF)
    {
         for( i = 1 ; i <= n ;i++)
            scanf("%d",&a[i]) ;
         memset(dp,0,sizeof(dp)) ;
         for( i = 1 ; i <= n ;i++)
         {
             for( j = 1 ; j <= m ;j++)
             {
                 dp[i][j]=dp[i-1][j-1]+a[i];
             }
             dp[i][0]=max(dp[i-1][0],dp[i-1][1]) ;
             for(int k=1;k<=m;k++)if(i-k>=0)
                dp[i][0]=max(dp[i][0],dp[i-k][k]);
         }
         cout << dp[n][0] << endl;
    }
    return 0 ;
}

时间: 2024-11-05 06:01:11

最近做的DP类题的总结~的相关文章

青蛙的烦恼(dp好题)

有n片荷叶正好在一凸多边形顶点上 有一只小青蛙恰好站在1号荷叶的点 小青蛙可以从一片荷叶上跳到另外任意一片荷叶上 给出N个点的坐标N<800 求小青蛙想通过最短的路程遍历所有的荷叶一次且仅一次的最短路径. 这题如果没有凸多边形的性质,就是裸的TSP问题,数据范围没法做的很大,用dp做也最多做到n=20左右,即使用更高级的退火模拟算法也只能到40左右. 但是这题的点在凸多边形上,因此有下面的性质: 青蛙遍历的路径不会相交. 证明很简单,画个图,利用三角形两边之和大于第三边即可. 结论:青蛙在1号结

树形DP经典题

题目传送门 题意: 给出一棵树,求离每个节点最远的点的距离 思路: 把无根树转化成有根树分析, 对于上面那棵树,要求距结点2的最长距离,那么,就需要知道以2为顶点的子树(蓝色圈起的部分,我们叫它Tree(2)),距顶点2的最远距离L1 还有知道2的父节点1为根节点的树Tree(1)-Tree(2)部分(即红色圈起部分),距离结点1的最长距离+dist(1,2) = L2,那么最终距离结点2最远的距离就是max{L1,L2} f[i][0],表示顶点为i的子树的,距顶点i的最长距离 f[i][1]

ACM :漫漫上学路 -DP -水题

CSU 1772 漫漫上学路 Time Limit: 1000MS   Memory Limit: 131072KB   64bit IO Format: %lld & %llu Submit Status Description 对于csuxushu来说,能够在CSU(California State University)上学是他一生的荣幸.CSU校园内的道路设计的十分精巧,由n+1条水平道路和n+1条竖直道路等距交错而成,充分体现了校园深厚的文化底蕴.然而不幸的是CS市每到夏季,天降大雨,

URAL 1018 Binary Apple Tree 树形DP 好题 经典

1018. Binary Apple Tree Time limit: 1.0 secondMemory limit: 64 MB Let's imagine how apple tree looks in binary computer world. You're right, it looks just like a binary tree, i.e. any biparous branch splits up to exactly two new branches. We will enu

Codeforces 148D 一袋老鼠 Bag of mice | 概率DP 水题

除非特别忙,我接下来会尽可能翻译我做的每道CF题的题面! Codeforces 148D 一袋老鼠 Bag of mice | 概率DP 水题 题面 胡小兔和司公子都认为对方是垃圾. 为了决出谁才是垃圾,大哥拿来了一袋老鼠,其中有w只白老鼠和b只黑老鼠.胡小兔先抓,先抓到白老鼠的人赢. 每次学姐抓完老鼠之后,总会有另外一只老鼠从袋子里自己跑出来(这只老鼠不算任何人抓的),而胡小兔抓老鼠时则不会发生这样的事. 每次袋子里的每只老鼠被抓到的概率相等,当有一只老鼠跑出来的时候,每只老鼠跑出来的几率也相

【状压DP水题】[USACO06NOV]玉米田Corn Fields

题目描述 Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can't b

石子合并 区间DP模板题

题目链接:https://vjudge.net/problem/51Nod-1021 题意 N堆石子摆成一条线.现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价.计算将N堆石子合并成一堆的最小代价. 例如:1 2 3 4 ,有不少合并方法 1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19) 1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24) 1 2 3

DP刷题记录

目录 dp刷题记录 codeforces 706C codeforces 940E BZOJ3997 POJ2279 GYM102082B GYM102082D codeforces132C L3-020 至多删三个字符 牛客 553C Chino with Queue POJ3260 The Fewest Coins Codeforces 372C dp刷题记录 codeforces 706C 题意:给出n个字符串,可以对每个字符串进行翻转操作, 每个操作对应一个消耗c[i],问经过操作后是否

hdu 5001 walk 概率dp入门题

Description I used to think I could be anything, but now I know that I couldn't do anything. So I started traveling. The nation looks like a connected bidirectional graph, and I am randomly walking on it. It means when I am at node i, I will travel t