提纲:
区间dp一般设计f[i][j]表示区间i到j的dp值,用几段小的合并成一段整体,也是分治的思想,转移时枚举中间点k,从f[i][k],f[k+1][j]来合并
1.题目:
题解:石子归并
水题开头
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n,a[105],sum[105],f[105][105];
int main()
{
int i,j,k;
scanf("%d",&n);
for (i=1;i<=n;i++)
scanf("%d",&a[i]),sum[i]=a[i]+sum[i-1];
//f[i][j]从i合并到j的最小值
memset(f,0x7f,sizeof(f));
for (i=1;i<=n;i++)
f[i][i]=0;
for (i=1;i<=n;i++)//区间大小
for (j=1;j<=n-i+1;j++)//起点
for (k=j;k<j+i-1;k++)
f[j][j+i-1]=min(f[j][j+i-1],f[j][k]+f[k+1][j+i-1]+sum[j+i-1]-sum[j-1]);
printf("%d",f[1][n]);
}
2.题目: 圆形石子归并
题解:
水题的扩展版本
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#define LL long long
using namespace std;
int n,a[105];
LL f[105][105],sum[105];
int main()
{
int i,j,k;
scanf("%d",&n);
for (i=1;i<=n;i++)
scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
for (i=n+1;i<=n*2;i++) a[i]=a[i-n],sum[i]=sum[i-1]+a[i];
memset(f,0x7f,sizeof(f));
LL ans1=f[0][0];//随意一个最大值
for (i=1;i<=2*n;i++) f[i][i]=0;
for (i=1;i<=n;i++)
for (j=1;j<=2*n-i;j++)
for (k=j;k<j+i-1;k++)
f[j][j+i-1]=min(f[j][j+i-1],f[j][k]+f[k+1][j+i-1]+sum[j+i-1]-sum[j-1]);
for (i=1;i<=n;i++)
ans1=min(ans1,f[i][i+n-1]);
LL ans2=0;
memset(f,0,sizeof(f));
for (i=1;i<=n;i++)
for (j=1;j<=2*n-i;j++)
for (k=j;k<j+i-1;k++)
f[j][j+i-1]=max(f[j][j+i-1],f[j][k]+f[k+1][j+i-1]+sum[j+i-1]-sum[j-1]);
for (i=1;i<=n;i++)
ans2=max(ans2,f[i][i+n-1]);
printf("%lld\n%lld",ans1,ans2);
}
3.题目:能量项链
题解:也是一样的,按照他说的合并加分就好了
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int a[105],f[205][405];
int main()
{
int n,i,j,k;
scanf("%d",&n);
for (i=1;i<=n;i++) scanf("%d",&a[i]);
for (i=n+1;i<=2*n;i++) a[i]=a[i-n];
for (i=1;i<=n;i++)
for (j=1;j<=2*n-i;j++)
for (k=j;k<j+i-1;k++)
f[j][j+i-1]=max(f[j][j+i-1],f[j][k]+f[k+1][j+i-1]+a[j]*a[j+i]*a[k+1]);
int ans=0;
for (i=1;i<=n;i++)
ans=max(ans,f[i][i+n-1]);
printf("%d",ans);
}
4.题目:玩具起名
题解:f[i][j][0/1/2/3]表示从i字母到j字母能否转化为W/I/N/G
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
//f[i][j]['W'/'I'/'N'/'G']表示从i字母到j字母能否转化为W/I/N/G
using namespace std;
struct hh{int x,y,z;}zh[80];
bool f[205][205][100];
char st[250];
int main()
{
int W,I,N,G,i,cnt=0,j,k,tt;
scanf("%d%d%d%d\n",&W,&I,&N,&G);
for (i=1;i<=W;i++) scanf("%s",st),zh[++cnt].x=st[0],zh[cnt].y=st[1],zh[cnt].z='W';
for (i=1;i<=I;i++) scanf("%s",st),zh[++cnt].x=st[0],zh[cnt].y=st[1],zh[cnt].z='I';
for (i=1;i<=N;i++) scanf("%s",st),zh[++cnt].x=st[0],zh[cnt].y=st[1],zh[cnt].z='N';
for (i=1;i<=G;i++) scanf("%s",st),zh[++cnt].x=st[0],zh[cnt].y=st[1],zh[cnt].z='G';
scanf("%s",st+1);int len=strlen(st+1);
for (i=1;i<=len;i++) f[i][i][st[i]]=true;
for (i=2;i<=len;i++)
for (j=1;j<=len-i+1;j++)
for (k=j;k<=j+i-1;k++)
for (tt=1;tt<=cnt;tt++)
f[j][i+j-1][zh[tt].z]|=f[j][k][zh[tt].x] && f[k+1][i+j-1][zh[tt].y];
bool fff=false;
if (f[1][len]['W']) fff=true,printf("W");
if (f[1][len]['I']) fff=true,printf("I");
if (f[1][len]['N']) fff=true,printf("N");
if (f[1][len]['G']) fff=true,printf("G");
if (!fff) printf("The name is wrong!");
}
题目:删数
题解:这题目一读就觉得是区间dp嘛
代码:
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
int a[105],f[105][105];
int cost(int i,int j){if (i==j) return a[i];else return abs(a[j]-a[i])*(j-i+1);}
int main()
{
int n,i,j,k;
scanf("%d",&n);
for (i=1;i<=n;i++) scanf("%d",&a[i]);
for (i=1;i<=n;i++) f[i][i]=a[i];
//f[i][j]从i合并到j的最大值
for (i=2;i<=n;i++)
for (j=1;j<=n-i+1;j++)
{
int r=j+i-1;
f[j][r]=cost(j,r);
for (k=j;k<r;k++)
f[j][r]=max(f[j][r],f[j][k]+f[k+1][r]);
}
printf("%d",f[1][n]);
}