DP专题(有可能有的题目不是dp)

POJ3006
数据范围1e6,所以直接线性筛把素数筛出来就可以了。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e6+7;
ll isprime[maxn],prime[maxn];
ll f[maxn];
int cnt = 0;
void getprime()
{
	memset(isprime,1,sizeof(isprime));
	isprime[0] = isprime[1] = 0;
	for(int i=2;i<maxn;i++)
	{
		if(isprime[i])
		{
			prime[++cnt] = i;
		}
		for(int j=1;j<=cnt && i*prime[j]<maxn;j++)
		{
			isprime[i*prime[j]] = 0;
			if(i%prime[j]==0) break;
		}
	}
	memset(isprime,0,sizeof(isprime));
	for(int i=1;i<=cnt;i++)
	{
		isprime[prime[i]] = 1;
	}
}
int main()
{
	int a,d,n;
	getprime();
	while(~scanf("%d%d%d",&a,&d,&n))
	{
		if(a==0 && d==0 && n==0) break;
		ll sum = a;
		int tmp = n;
		while(tmp)
		{
			if(isprime[sum]) tmp--;
			sum += d;
		}
		printf("%lld\n",sum-d);
	}
	return 0;
}

POJ2181
动态规划
dp[i][1]表示上一次是加上权值,dp[i][0]表示上一次减掉权值。
则dp[i][1]要么不变,要么减掉权值,dp[i][0]要么不变,要么加上权值。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 2e5+7;
int a[maxn];
int dp[maxn][2];
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",a+i);
	}
	dp[1][0] = a[1];
	for(int i=2;i<=n;i++)
	{
		dp[i][1] = max(dp[i-1][1],dp[i-1][0]-a[i]);
		dp[i][0] = max(dp[i-1][0],dp[i-1][1]+a[i]);
	}
	cout<<max(dp[n][1],dp[n][0])<<endl;
	return 0;
}

POJ2385
dp[i][j][k]表示前i分钟,已经移动了j次,当前在k树下的最大值。
那么对于一个状态,要么是移动,要么不移动,然后更新一下最大值就行了。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1010;
int dp[maxn][35][3];
int a[maxn];
int main()
{
	int t,w;
	cin>>t>>w;
	for(int i=1;i<=t;i++)
	{
		cin>>a[i];
	}
	if(a[1]==1)
	{
		dp[1][0][1] = 1;
	}
	else
	{
		dp[1][0][2] = 1;
	}
	int ans = 0;
	for(int i=2;i<=t;i++)
	{
		if(a[i]==1)
		{
			dp[i][0][1] = dp[i-1][0][1]+1;
			dp[i][0][2] = dp[i-1][0][2];
		}
		else
		{
			dp[i][0][1] = dp[i-1][0][1];
			dp[i][0][2] = dp[i-1][0][2]+1;
		}
		for(int j=1;j<=w;j++)
		{
			if(a[i]==1)
			{
				dp[i][j][1] = max(dp[i-1][j][1]+1,dp[i-1][j-1][2]+1);
				dp[i][j][2] = dp[i-1][j][2];
			}
			else
			{
				dp[i][j][2] = max(dp[i-1][j][2]+1,dp[i-1][j-1][1]+1);
				dp[i][j][1] = dp[i-1][j][1];
			}
			ans = max(ans,max(dp[i][j][1],dp[i][j][2]));
		}
	}
	printf("%d\n",ans);
	return 0;
}

POJ1159
由于我们可以在任意位置插入字符,我们就直接求出本来串的最大回文长度,答案是len-dp[len][len]。

#include <iostream>
#include <string> 
#include <algorithm>
using namespace std;
const int maxn = 5010;
int dp[2][maxn];
int main()
{
	int n;
	cin>>n;
	string s;
	cin>>s;
	string p = s;
	reverse(s.rbegin(),s.rend());
	s.insert(0,"&");
	p.insert(0,"#");
	for(int i=1;i<s.size();i++)
	{
		for(int j=1;j<p.size();j++)
		{
			if(s[i]==p[j])
			{
				dp[i%2][j] = dp[(i-1)%2][j-1]+1;
			}
			else
			{
				dp[i%2][j] = max(dp[i%2][j-1],dp[(i-1)%2][j]);
			}
		}
	}
	cout<<s.size()-1-dp[(s.size()-1)%2][p.size()-1]<<endl;
	return 0;
}

POJ1050
我们要求最大子矩阵,是个二维的问题。我们就对每行求最大子序列,然后枚举列,就可以更新出一个矩阵的最大值了。

#include <iostream>
#include <cstdio>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 200;
int a[maxn][maxn];
int sum[maxn][maxn];
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&a[i][j]);
			sum[i][j] = sum[i][j-1]+a[i][j];
		}
	}
	int ans = -INF;
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j++)
		{
			int tmp = 0;
			for(int k=1;k<=n;k++)
			{
				tmp += sum[k][j]-sum[k][i-1];
				if(tmp<0)
				{
					tmp = 0;
				}
				ans = max(ans,tmp);
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

POJ2378
树形dp裸题,dp[i]表示以i为根节点的左右子树中最大的节点数,那么显然如果最大的子树都小于等于(n/2),那么所有的都满足条件了。

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 1e5+7;
vector<int> G[maxn];
int dp[maxn];
int size[maxn];
int n;
void dfs(int now,int fa)
{
	size[now] = 1;
	dp[now] = 1;
	for(int i=0;i<G[now].size();i++)
	{
		int v = G[now][i];
		if(v!=fa)
		{
			dfs(v,now);
			size[now] += size[v];
			dp[now] = max(dp[now],size[v]);
		}
	}
	dp[now] = max(dp[now],n-size[now]);
}
int main()
{
	scanf("%d",&n);
	int x,y;
	for(int i=0;i<n-1;i++)
	{
		scanf("%d%d",&x,&y);
		G[x].push_back(y);
		G[y].push_back(x);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)
	{
		if(dp[i]<=n/2) cout<<i<<endl;
	}
	return 0;
}

CF1088E
和上一题很相似,不过这个题目需要记录一下最大值的数目,跑两次dfs即可。

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pb push_back
using namespace std;
const int maxn = 3e5+7;
typedef long long ll;
vector<int> G[maxn];
ll v[maxn];
int n;
ll size[maxn];
ll dp[maxn];
ll ans = -INF;
int cnt = 0;
void dfs(int now,int fa)
{
	dp[now] = v[now];
	for(int i=0;i<G[now].size();i++)
	{
		int v = G[now][i];
		if(v!=fa)
		{
			dfs(v,now);
			if(dp[v]>0) dp[now] += dp[v];
		}
	}
	ans = max(ans,dp[now]);
}
void dfs2(int now,int fa)
{
	dp[now] = v[now];
	for(int i=0;i<G[now].size();i++)
	{
		int v = G[now][i];
		if(v!=fa)
		{
			dfs2(v,now);
			if(dp[v]>0) dp[now] += dp[v];
		}
	}
	if(dp[now]==ans)
	{
		cnt++;
		dp[now] = 0;
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",v+i);
	}
	for(int i=0;i<n-1;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		G[x].pb(y);
		G[y].pb(x);
	}
	dfs(1,0);
	dfs2(1,0);
	cout<<ans*cnt<<" "<<cnt<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/KIKO_caoyue/article/details/84942921