今日dp总结
从下午一点到晚上十点半写了五个dp题,有水题也有没搞懂的偏难(对我来说)题,写在这里记录一下。
P7822 「RdOI R3」学习算法
共有n个空格,m个数,每个空格仅能填一个数,第i个数最多连续填a[i]次,求填满空格的总方案数。
核心是连续填数的个数有限制条件,一维仅能表示目前是多少天,不能推出连续填数的限制,所以再开一维表示当前天填了哪个数。
巧妙的状态转移方程+巧妙的优化复杂度方式(对我来说
#include<bits/stdc++.h>
using namespace std;
int n,m;const int N=7e3+10,mod=1e9+7;
int f[N][N];
int a[N];
long long sum[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++) cin>>a[i];
if(m==1&&a[m]<n)
{
cout<<0;
return 0;
}
for(int i=1;i<=m;i++) f[1][i]=1;
sum[1]=m;
for(int i=2;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
f[i][j]=sum[i-1];
if(i>a[j]+1) f[i][j]=(f[i][j]-sum[i-a[j]-1]+f[i-a[j]-1][j]+mod)%mod;
else if(i==a[j]+1) f[i][j]=(f[i][j]-1+mod)%mod;
sum[i]=(sum[i]+f[i][j])%mod;
}
}
cout<<sum[n];
}
P5858 「SWTR-03」Golden Sword
单调队列优化线性dp板子题
太蒟蒻了还是没理解单调队列部分的实现方式,只能勉强打出n^3的做法,然后水了个题解(我有罪)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,w,s;
const int N=5e3+100;
ll dp[N][N],a[N];
deque<ll> d;
int main()
{
memset(dp,-0x3f,sizeof dp);
cin>>n>>w>>s;
for(int i=1;i<=n;i++) cin>>a[i];
dp[1][1]=a[1];
for(int i=2;i<=n;i++)
{
d.clear();
for(int j=w;j>=min(i-1,w);j--)
{
while(!d.empty()&&dp[i-1][d.back()]<dp[i-1][j]) d.pop_back();
d.push_back(j);
}
for(int j=min(i,w);j>=1;j--)
{
while(!d.empty()&&d.front()>j+s-1) d.pop_front();
if(j>1)
{
while(!d.empty()&&dp[i-1][d.back()]<=dp[i-1][j-1]) d.pop_back();
d.push_back(j-1);
}
dp[i][j]=dp[i-1][d.front()]+a[i]*j;
}
}
ll ans=-1e16;
for(int i=1;i<=w;i++) ans=max(dp[n][i],ans);
cout<<ans;
}
P1063 [NOIP2006 提高组] 能量项链
环拆链区间dp板子题,不用多说
调左右合并的时候k值调挂了一发喜提爆零
打表debug大法好
#include<bits/stdc++.h>
using namespace std;
const int N=210;
int n,f[N][N];
struct node{
int l,r;
}a[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int x;cin>>x;
if(i==1) a[i].l=a[n].r=a[i+n].l=a[i+n].r=x;
else a[i-1].r=a[i].l=a[n+i-1].r=a[i+n].l=x;
}
for(int len=2;len<=n;len++)
{
for(int i=1;i<=2*n-len;i++)
{
if(len==2) f[i][i+len-1]=a[i].l*a[i].r*a[i+1].r;
else
{
for(int k=i;k<i+len-1;k++)
f[i][i+len-1]=max(f[i][i+len-1],f[i][k]+f[k+1][i+len-1]+a[i].l*a[k].r*a[i+len-1].r);
}
}
}
int ans=-0x3f3f3f3f;
for(int i=1;i<=n;i++)
ans=max(ans,f[i][i+n-1]);
cout<<ans;
}
P1057 [NOIP2008 普及组] 传球游戏
最简单的dp了,唯一注意由于是环形所以需要i=1和i=n特判一下
#include<bits/stdc++.h>
using namespace std;
const int N=40;
long long f[N][N];//f[i][j]传了i次在j手中
int n,m;
int main()
{
cin>>n>>m;
f[0][1]=1;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if(j==1) f[i][j]=f[i-1][j+1]+f[i-1][n];
else if(j==n) f[i][j]=f[i-1][j-1]+f[i-1][1];
else f[i][j]=f[i-1][j-1]+f[i-1][j+1];
}
}
cout<<f[m][1];
}
P1052 [NOIP2005 提高组] 过河
最简单线性dp+路径压缩,于是成为了绿题
线性dp的部分没话说,1e9的数据范围需要路径压缩,数论令人头大,看了好多题解硬是没看懂,可能是小学数学没学好,呜呜
#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
bool st[N];
int len,l,s,t,m,f[N];
int stone[110];
main()
{
cin>>l>>s>>t>>m;
memset(f,0x3f,sizeof f);
for(int i=1;i<=m;i++) cin>>stone[i];
sort(stone+1,stone+1+m);
for(int i=1;i<=m;i++)
{
if(stone[i]-stone[i-1]<=s*t) len+=stone[i]-stone[i-1];
else len+=(stone[i]-stone[i-1])%t+t;
st[len]=1;
}
f[0]=0;
for(int i=1;i<=len+t;i++)
{
for(int j=s;j<=t;j++)
if(i-j>=0) f[i]=min(f[i],f[i-j]+st[i]);
}
int ans=0x3f3f3f3f3f;
for(int i=len;i<=len+t;i++)
ans=min(ans,f[i]);
cout<<ans;
}

浙公网安备 33010602011771号