poj 几道简单的dp题

题目:poj 1836

题意:
求使数列程先递增后递减的形式需要去掉的数字个数。当然也可以直接递减或者只递减不递增。
分析:
用最长递增子序列的方法求,然后枚举两个起点的位置即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=1e8;
const int N=1000+9;
void LIS(int n,int d[],double g[],double A[]) //Nlog(N)
{
    for(int i=1;i<=n;i++)g[i]=INF;
    for(int i=0;i<n;i++){
        int k=lower_bound(g+1,g+1+n,A[i])-g;
        d[i]=k;
        g[k]=A[i];
    }
}
int d1[N],d2[N];
double a[N],b[N],g[N];
int n;
int main()
{
   // freopen("f.txt","r",stdin);
    scanf("%d",&n);
    for(int i=0;i<n;i++)scanf("%lf",&a[i]),b[n-i-1]=a[i]; //b翻转过来
    LIS(n,d1,g,a);
    LIS(n,d2,g,b);
    int maxn=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<n-i-1;j++)maxn=max(maxn,d1[i]+d2[j]);
    printf("%d
",n-maxn);
    return 0;
}

题目:poj 1260

题意:
给出几类珍珠,以及它们的单价,要求用最少的钱就可以买到相同数量的,相同(或更高)质量的珍珠。
【规定买任一类的珍珠n个(价格为p),都要支付(n+10)*p的钱,即额外支付10*p】
分析:
f[i]表示到第i个品类最少花费。
那么f[i]=min{ f[j]+(num[j+1]+num[j+2]+….+num[i])*p[i] }(j

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=1e8;
const int N=100+9;
/*
f[i]表示到第i个品类最少花费。
那么f[i]=min{ f[j]+(num[j+1]+num[j+2]+....+num[i])*p[i] }
*/
int n,ans;
int num[N],p[N],f[N];
int main()
{
  // freopen("f.txt","r",stdin);
    int T;scanf("%d",&T);
    while(T--){
        ans=INF;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&num[i],&p[i]);
        }
        f[0]=0;
        for(int i=1;i<=n;i++){
            int minn=INF,cnt=num[i];
            for(int j=i-1;j>=0;j--){
                minn=min(minn,f[j]+(cnt+10)*p[i]);
                cnt+=num[j];
            }
            f[i]=minn;
        }

        printf("%d
",f[n]);
    }
    return 0;
}

poj 1080

题意:
LCS变形题,根据LCS的思想去推dp方程即可。

#include<cstdio>
#include<iostream>
using namespace std;
const int mod=998244353;
const int N=111;
int w[300][300],f[N][N];
char s1[111],s2[111];
int main()
{
    //freopen("f.txt","r",stdin);
    w['A']['A']=w['C']['C']=w['G']['G']=w['T']['T']=5;
    w['A']['C']=w['C']['A']=w['A']['T']=w['T']['A']=w['T']['-']=w['-']['T']=-1;
    w['A']['G']=w['G']['A']=w['C']['T']=w['T']['C']=w['G']['-']=w['-']['G']=-2;
    w['G']['T']=w['T']['G']=-2;
    w['A']['-']=w['-']['A']=w['C']['G']=w['G']['C']=-3;
    w['C']['-']=w['-']['C']=-4;
    int T;scanf("%d",&T);
    while(T--){
        int n1;scanf("%d%s",&n1,s1+1);
        int n2;scanf("%d%s",&n2,s2+1);
        int ans=0;
        f[0][0]=0;
        for(int i=1;i<=n1;i++)f[i][0]=f[i-1][0]+w[s1[i]]['-'];
        for(int j=1;j<=n2;j++)f[0][j]=f[0][j-1]+w['-'][s2[j]];
        for(int i=1;i<=n1;i++){
            for(int j=1;j<=n2;j++){
                f[i][j]=f[i-1][j-1]+w[s1[i]][s2[j]];
                f[i][j]=max(f[i][j],f[i-1][j]+w[s1[i]]['-']);
                f[i][j]=max(f[i][j],f[i][j-1]+w['-'][s2[j]]);
            }
        }
        printf("%d
",f[n1][n2]);
    }
    return 0;
}

poj 1159

题意:
给一个字符串,问最少插入几个字符可以构成回文串?
分析:
求一下正串和逆串的最长公共子序列,就是最大可以匹配的,那么最少插入的就是n-LCS。
这题n<=5000,用int f[N][N]会超内存,可以改成short,也可以用滚动数组。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5001;
char a[N],b[N];
short f[2][N];
int n;
int main()
{
    //freopen("f.txt","r",stdin);
    scanf("%d%s",&n,a+1);
    for(int i=1;i<=n;i++)b[n-i+1]=a[i];
    f[0][0]=0;
    int k=0;
    for(int i=1;i<=n;i++){
        k=1^k;
        for(int j=1;j<=n;j++){
            if(a[i]==b[j])f[k][j]=f[k^1][j-1]+1;
            else f[k][j]=max(f[k^1][j],f[k][j-1]);
        }
    }
    printf("%d
",n-f[k][n]);
    return 0;
}

poj 2479

题意:
给出一个整数序列,求最大的两段连续序列和。
分析:
类似于poj1836那题,求一下从左端开始的最大序列和f[i],从右端开始的最大序列和d[i]。然后枚举中点即可。
f[i]表示从左端到i点的一段最大的连续序列和
d[i]表示从右端到i点的一段最大的连续序列和

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int INF=5e8;
const int N=50001;
int f[N],d[N],a[N];
int n;
int main()
{
    //freopen("f.txt","r",stdin);
    int T;scanf("%d",&T);
    while(T--){
        memset(f,0,sizeof(f));
        memset(d,0,sizeof(d));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        int sum=a[1],maxn=a[1];
        f[1]=a[1];
        for(int i=2;i<=n;i++){
            if(sum<0)sum=0;
            sum+=a[i],maxn=max(sum,maxn);
            f[i]=maxn;
        }
        sum=maxn=d[n]=a[n];
        for(int i=n-1;i>=1;i--){
            if(sum<0)sum=0;
            sum+=a[i],maxn=max(sum,maxn);
            d[i]=maxn;
        }
        int ans=-INF;
        for(int i=1;i<n;i++){

            ans=max(ans,f[i]+d[i+1]);
        }
        printf("%d
",ans);
    }
    return 0;
}

/*
3
10
1 -1 2 2 3 -3 4 -4 5 -5
2
1 -1
3
1 -1 2
*/