今日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;
}
posted @ 2022-04-06 00:10  Hssliu  阅读(37)  评论(1)    收藏  举报