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",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\n",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\n",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",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\n",ans);
}
return 0;
}
/*
3
10
1 -1 2 2 3 -3 4 -4 5 -5
2
1 -1
3
1 -1 2
*/