被网络流虐了一天后,本蒟蒻又来做DP啦!(还是被虐
P4933 大师
一句话题意:求一个数列的等差子数列。
我看正解好像是(n^2)的,可蒟蒻的我只能想到(n^3),不过数据水,还是让我卡过去了
¥( ≧ ▽ ≦ )¥
开始讲算法吧:设(f[i][j])为等差数列最后一项为(i),倒数第二项为(j)的方案数,这种状态好像有点奇怪,但还是可以做滴。
状态定义出来了,转移就简单了,三重循环暴力枚举,当(a[i]-a[j]==a[j]-a[k])时,(f[j][i]+=f[k][j]),最后统计答案,欧了。
废话不多说,上代码:
#include<bits/stdc++.h>
#define re register
#define mod 998244353
using namespace std;
const int N=1005;
int n,a[N];
long long ans,f[N][N];
int main()
{
scanf("%d",&n);
for(re int i=1;i<=n;i++)scanf("%d",&a[i]);
for(re int i=1;i<=n;i++)
{
f[i][i]=1;ans=(ans+1)%mod;
for(re int j=1;j<i;j++)
{
f[j][i]=1;
for(re int k=j-1;k>0;k--)
{
if(a[i]-a[j]==a[j]-a[k])
f[j][i]=(f[j][i]+f[k][j])%mod;
}
ans=(f[j][i]+ans)%mod;
}
}
printf("%lld",ans);
return 0;
}
P5858 SWTR-03」Golden Sword
这题就比较好玩了,想了十来分钟,都只是一个(O(nws))时间复杂度的算法,所以就只能想想优化了啦。
这题状态比较好想,(f[i][j])为放完了前(i)个后,锅内还剩(j)个的耐久度最大值。同时转移方程也比较简单:
(~~~~~~~~~~~~~~~~~~~~~~~~ f[i][j]=max~){(~f[i][k]~)}(~+a[i] imes j)
其中(j-1le kle j+s-1)
然后你就会发现,(TLE)满天飞。
去哦德玛嘚!
求一个连续区间的最大值,而这个区间在一个大区间上滑动!咋这么眼熟呢?不就是滑动窗口吗?单调队列上!
在就没什么可讲了,至于单调队列优化,可以借鉴代码:
#include<bits/stdc++.h>
#define re register
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define inf 1e18
using namespace std;
const int N=5555;
int n,w,s,st[N],l,r;
long long a[N],que[N],f[N][N],ans=-inf;
int main()
{
scanf("%d%d%d",&n,&w,&s);
for(re int i=1;i<=n;i++)scanf("%lld",&a[i]);
memset(f,-63,sizeof(f));
f[0][0]=0;
for(re int i=1;i<=n;i++)
{
l=1,r=0;
for(re int j=0;j<=s;j++)
{
que[++r]=f[i-1][j],st[r]=j;
for(;r>l&&que[r]>=que[r-1];r--)st[r-1]=st[r],que[r-1]=que[r];
}
for(re int j=1;j<=min(w,i);j++)
{
if(st[l]<j-1)l++;
f[i][j]=que[l]+j*a[i];
if(j+s<=min(i,w))
{
que[++r]=f[i-1][j+s],st[r]=j+s;
for(;r>l&&que[r]>=que[r-1];r--)st[r-1]=st[r],que[r-1]=que[r];
}
}
}
// for(re int i=1;i<=n;i++)
// {
// for(re int j=1;j<=w;j++)
// printf("%d ",f[i][j]);
// puts("");
// }
for(re int i=1;i<=w;i++)ans=max(ans,f[n][i]);
printf("%lld",ans);
return 0;
}
P3205 [HNOI2010]合唱队
这题输入给的是排好队后的序列,因为不知道每个人是从哪边插入的队列,所以需要一维数组表示插入方向。
因此,状态就出来了,(f[i][j][0/1])表示从(i)号位置至前(j)位的序列,且最后一个人是从哪个方向插入的((0)表示左,(1)表示右
所以状态转移方程就出来了:
[f[i][j][1]+=f[i-1][j-1][1]~~~~ a[i]>a[i-1]
]
[f[i][j][1]+=f[i-1][j-1][0]~~~~ a[i]>a[i-j+1]
]
[f[i][j][0]+=f[i][j-1][0]~~~~ a[i-j+1]<a[i-j+2]
]
[f[i][j][0]+=f[i][j-1][1]~~~~ a[i-j+1]<a[i]
]
要注意的一点是:在(j=2)时,(i-1=i-j+1),会重复加,所以要特判。
手起,码落:
#include<bits/stdc++.h>
#define re register
#define mod 19650827
using namespace std;
const int N=1005;
int n,a[N],f[N][N][2],ans;
int main()
{
scanf("%d",&n);
for(re int i=1;i<=n;i++)scanf("%d",&a[i]);
for(re int i=1;i<=n;i++)f[i][1][0]=f[i][1][1]=1;
for(re int i=2;i<=n;i++)
{
if(a[i]>a[i-1])f[i][2][0]=f[i][2][1]=1;
for(re int j=3;j<=i;j++)
{
if(a[i]>a[i-1])(f[i][j][1]+=f[i-1][j-1][1])%=mod;
if(a[i]>a[i-j+1])(f[i][j][1]+=f[i-1][j-1][0])%=mod;
if(a[i-j+1]<a[i-j+2])(f[i][j][0]+=f[i][j-1][0])%=mod;
if(a[i-j+1]<a[i])(f[i][j][0]+=f[i][j-1][1])%=mod;
}
}
printf("%d",(f[n][n][0]+f[n][n][1])%mod);
return 0;
}
P4170 [CQOI2007]涂色
我是一个小画家,手拿画笔来画画~~
这题的状态应该也比较好想吧?(f[l][r])表示(l)到(r)区间所需的最小填图数。而状态转移方程如下:
[f[l][r]=min(f[l+1][r],f[l][r-1])~~~ a[l]=a[r]
]
[f[l][r]=min(f[l][k]+f[k+1][r])~~~ a[l]
eq a[r]&&lle k<r
]
第一个应该没问题,那第二个为什么呢?先来看看有什么性质吧,我们其实可以发现,最少的填图次数的方式,一定是如下的:
也就是说,填图不会跨过两个有不同颜色的区间。所以,一定存在一个点,把整个区间分开,而隔开数量不超过(1)的区间。
又讲完了,手起,码落:
#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=55;
int n,f[N][N];
char ch[N];
int main()
{
scanf("%s",ch+1);
n=strlen(ch+1);
memset(f,63,sizeof(f));
for(re int i=1;i<=n;i++)f[i][i]=1;
for(re int i=1;i<=n;i++)
for(re int j=i-1;j>0;j--)
if(ch[i]==ch[j]) f[j][i]=min(f[j+1][i],f[j][i-1]);
else for(re int k=j;k<i;k++)
f[j][i]=min(f[j][i],f[j][k]+f[k+1][i]);
printf("%d",f[1][n]);
return 0;
}
CF607B Zuma
好一个经典的游戏,好一个我不会写的题
其实这题和上一题差不了多少,可以自己想一想啦!(就是我自己不想打了
想好了可以对对代码:
#include<bits/stdc++.h>
#define re register
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
const int N=505;
int n,a[N],f[N][N];
int main()
{
scanf("%d",&n);
for(re int i=1;i<=n;i++)scanf("%d",&a[i]);
memset(f,63,sizeof(f));
for(re int i=1;i<=n;i++)f[i][i]=1;
for(re int i=1;i<=n;i++)
for(re int j=i-1;j>0;j--)
{
if(a[i]==a[j])
{
if(i-j==1)f[j][i]=1;
else f[j][i]=f[j+1][i-1];
}
for(re int k=j;k<i;k++)
f[j][i]=min(f[j][i],f[j][k]+f[k+1][i]);
}
printf("%d",f[1][n]);
return 0;
}