crp week4 周报

B3637 最长上升子序列

1.思路:最常见的动规问题,用O(n^2)的复杂度就过了

2.代码:

#include <bits/stdc++.h>
using namespace std;
int a[5005];
int f[5005];
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i=i+1)
	{
		cin>>a[i];
	}
	for(int i=1;i<=n;i=i+1)
	{
		f[i]=1;
		for(int j=1;j<i;j=j+1)
		{
			if(a[i]>a[j])
			{
				f[i]=max(f[j]+1,f[i]);
			}
		}
	}
	sort(f+1,f+n+1);
	cout<<f[n];
}

P1115 最大子段和

1.思路:分两种情况:如果加上当前数为负数,那么就重新开始取子段;如果加上当前数大于0,那么就将这个数继续放入前面的子段内

2.代码:

#include <bits/stdc++.h>
using namespace std;
int n;
int a[200005];
int f[200005];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i=i+1)
	{
		cin>>a[i];
	}
	f[1]=a[1];
	for(int i=2;i<=n;i=i+1)
	{
		if(f[i-1]<0)
		f[i]=a[i];
		else
		{
			f[i]=f[i-1]+a[i];
		 } 
	}
	sort(f+1,f+n+1);
	cout<<f[n];
}

P8707 [蓝桥杯 2020 省 AB1] 走方格

1.思路:到达当前格子的方案数等于向下到达这一格的方案数+向右到达这一格的方案数

2.代码:

#include <bits/stdc++.h>
using namespace std;
int n,m;
long long f[35][35];
int main()
{
	cin>>n>>m;
	f[1][1]=1;
	for(int i=1;i<=n;i=i+1)
	{
		for(int j=1;j<=m;j=j+1)
		{
			if(i%2==0&&j%2==0)
			{
				continue;
			}
			else
			{
				if(j-1>0)
				{
					f[i][j]=f[i][j]+f[i][j-1];
				}
				if(i-1>0)
				{
					f[i][j]=f[i][j]+f[i-1][j];
				}
			}
		}
	}
	cout<<f[n][m];
}

P1216 [USACO1.5] [IOI1994]数字三角形 Number Triangles

1.思路:数塔问题的变形

2.代码:

#include <bits/stdc++.h>
using namespace std;
int a[1005][1005];
int f[1005][1005];
int main()
{
	int r;
	cin>>r;
	for(int i=1;i<=r;i=i+1)
	{
		for(int j=1;j<=i;j=j+1)
		{
			cin>>a[i][j];
		}
	}
	for(int j=1;j<=r;j=j+1)
	{
		f[r][j]=a[r][j];
	}
	for(int i=r-1;i>=1;i=i-1)
	{
		for(int j=1;j<=i;j=j+1)
		{
			f[i][j]=max(f[i+1][j]+a[i][j],f[i+1][j+1]+a[i][j]);
		}
	}
	cout<<f[1][1];
}

P1020 [NOIP1999 普及组] 导弹拦截

1.思路:最长子序列的升级版,需要O(nlogn)的复杂度才能过题,其中用到了二分查找的方法。另外还用到一个定理:最少链条划分数等于最长反链长度

2.代码:

#include <bits/stdc++.h>
using namespace std;
const int inf=2147483547;
const int maxn=1e5+3;
int n,t,h[maxn],f[maxn];
int main()
{
	while(~scanf("%d",&h[++n]));--n;
	t=0;
	memset(f,0,sizeof(f));
	f[0]=inf;
	for(int i=1;i<=n;i=i+1)
	{
		int l=0;
		int r=t+1;
		while(r-l>1)
		{
			int m=l+(r-l)/2;
			if(f[m]>=h[i])
			{
				l=m;
			}
			else
			{
				r=m;
			}
		}
		int x=l+1;
		if(x>t)t=x;
		f[x]=h[i]; 
	}
	cout<<t<<endl;
	t=0;
	memset(f,0,sizeof(f));
	f[0]=0;
	for(int i=1;i<=n;i=i+1)
	{
		int l=0;
		int r=t+1;
		while(r-l>1)
		{
			int m=l+(r-l)/2;
			if(f[m]<h[i])
			{
				l=m;
			}
			else
			{
				r=m;
			}
		}
		int x=l+1;
		if(x>t)
		{
			t=x;
		}
		f[x]=h[i];
	}
	cout<<t<<endl;
	return 0;
}

LCS

1.思路:分两种情况讨论:如果两个字符串枚举到的字符刚好相等,那么f[i][j]=f[i-1][j-1]+1;如果两个字符串字符不相等,那么f[i][j]=max(f[i-1][j],f[i][j-1]);

2.代码:

#include <bits/stdc++.h>
using namespace std;
int res[3005][3005];
char ans1[100010];
char s[100010];
char t[100010];
int dfs(int i,int j)
{
	if(res[i][j]!=-1)
	{
		return res[i][j];
	}
	int ans;
	if(i==0||j==0)
	{
		ans=0;
	}
	else if(s[i]==t[j])
	{
		ans=dfs(i-1,j-1)+1;
	}
	else
	{
		ans=max(dfs(i-1,j),dfs(i,j-1));
	}
	res[i][j]=ans;
	return ans;
}
int main()
{
	cin>>s+1>>t+1;
	memset(res,-1,sizeof(res));
	int as=strlen(s+1);
	int at=strlen(t+1);
	int num=dfs(as,at);
	int i = as, j = at;
	while(res[i][j] > 0)
    {
		if(s[i] == t[j])
        {
            ans1[res[i][j]] = s[i];
            i--, j--;
        }
		else
        {
			if(res[i][j] == res[i - 1][j]) i--;
			else j--;
		}
	}
	printf("%s", ans1+1);
    return 0;
}

P2196 [NOIP1996 提高组] 挖地雷

1.思路:第一眼发现用dfs可以做,尝试了一下就做出来了。

2.收获:判断是否继续深搜可以再写一个check函数,不容易弄混

3.代码:

#include <bits/stdc++.h>
using namespace std;
int a[25];
int f[25][25];
int vis[25];
long long max1=0;
int n;
vector<int> v;
int h[25];
bool check(int x)
{
	for(int i=1;i<=n;i=i+1)
	{
		if(f[x][i]&&!vis[i])
		{
			return 0;
		}
	}
	return 1;
}
void dfs(int x,long long z,int num1)
{
	if(check(x))
	{
		if(z>max1)
		{
			v.clear();
			for(int i=1;i<=num1;i=i+1)
			{
				v.push_back(h[i]);
			}
		}
		max1=max(max1,z);
	}
	else
	{
		for(int i=1;i<=n;i=i+1)
		{
			if(f[x][i]==1&&vis[i]==0)
			{
				vis[i]=1;
				h[num1+1]=i;
				dfs(i,z+a[i],num1+1);
				vis[i]=0;
			}
		}
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i=i+1)
	{
		cin>>a[i];
	}
	for(int i=1;i<n;i=i+1)
	{
		for(int j=i+1;j<=n;j=j+1)
		{
			cin>>f[i][j];
		}
	}
	for(int i=1;i<=n;i=i+1)
	{
		memset(vis,0,sizeof(vis));
		vis[i]=1;
		h[1]=i;
		dfs(i,a[i],1);
	}
	for(int i=0;i<v.size();i=i+1)
	{
		if(i==0)
		{
			cout<<v[i];
		}
		else
		{
			cout<<" "<<v[i];
		}
	}
	cout<<endl;
	cout<<max1;
}

P1541 [NOIP2010 提高组] 乌龟棋

1.思路:枚举四种卡片用了多少张,一种新的动归思路,记在小本本里

2.代码:

#include <bits/stdc++.h>
using namespace std;
int a[360];
int b[5];
int n,m;
int max1=0;
int f[125][125][125][125];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i=i+1)
	{
		cin>>a[i];
	}
	for(int i=1;i<=m;i=i+1)
	{
		int x;
		cin>>x;
		b[x]=b[x]+1;
	}
	f[0][0][0][0]=a[1];
	for(int i=0;i<=b[1];i=i+1)
	{
		for(int j=0;j<=b[2];j=j+1)
		{
			for(int k=0;k<=b[3];k=k+1)
			{
				for(int l=0;l<=b[4];l=l+1)
				{
					int r=1+i+j*2+k*3+l*4;
					if(l!=0)f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k][l-1]+a[r]);
					if(k!=0)f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k-1][l]+a[r]);
					if(j!=0)f[i][j][k][l]=max(f[i][j][k][l],f[i][j-1][k][l]+a[r]);
					if(i!=0)f[i][j][k][l]=max(f[i][j][k][l],f[i-1][j][k][l]+a[r]);
				}
			}
		}
	}
	cout<<f[b[1]][b[2]][b[3]][b[4]];
}

P2679 [NOIP2015 提高组] 子串

1.思路:滚动数组减少内存空间,但是动归方程实在是太难推了,看题解都看了半天

2.代码:

#include <bits/stdc++.h>
#define prime 1000000007
using namespace std;
char a[1005],b[205];
int n,m,t;
int f[2][205][205][2];//滚动数组,缩小内存空间 
int s,now,pre=1;
int main()
{
	cin>>n>>m>>t;
	scanf("%s%s",a+1,b+1);
	for(int i=1;i<=n;i=i+1)
	{
		swap(now,pre);
		f[now][1][1][0]=s;
		if(a[i]==b[1])
		{	
			f[now][1][1][1]=1;//初始条件:B串第一位和A串第i位匹配 ,方案数为1。 
			s++;//初始条件:统计第在未使用时,A串第1到i-1位中与B串第一位匹配的个数 
		}
		for(int j=2;j<=m;j=j+1)
		{
			for(int k=1;k<=t;k=k+1)
			{
				if(a[i]==b[j])//说明A串第i位可以被使用 
				{
					f[now][j][k][1]=((f[pre][j-1][k-1][1]+f[pre][j-1][k][1])%prime+f[pre][j-1][k-1][0])%prime;//第i-1位使用了,但第i位仍作为一个新子串+A串第i位与前面的字母组成合成子串 
				}
				f[now][j][k][0]=(f[pre][j][k][0]+f[pre][j][k][1])%prime;//A串第i位不可被使用, 方案数等于A串第i-1位未使用时,前i-1位匹配前j位使用k个字串+第i-1位使用时,前i-1位匹配前j位用k个子串 
			}
		}
		for(int j=1;j<=m;j=j+1)
		{
			for(int k=1;k<=t;k=k+1)
			{
				f[pre][j][k][1]=f[pre][j][k][0]=0;//一遍动规完成后进行清空 
			}
		}
	}
	cout<<(f[now][m][t][1]+f[now][m][t][0])%prime;
	return 0;
}

P5322 [BJOI2019] 排兵布阵

1.思路:贪心对城堡派出人数进行排序,动归是典型的背包问题,但是第一次解还是看了题解

2.代码:

#include <bits/stdc++.h>
using namespace std;
int a[105][105];
int f[20005];
int main()
{
	int s,n,m;
	cin>>s>>n>>m;
	for(int i=1;i<=s;i=i+1)
	{
		for(int j=1;j<=n;j=j+1)
		{
			cin>>a[j][i];
		}
	}
	for(int i=1;i<=n;i=i+1)
	{
		sort(a[i]+1,a[i]+s+1);
	}
	
	for(int i=1;i<=n;i=i+1)
	{
		for(int j=m;j>=0;j=j-1)
		{
			for(int k=1;k<=s;k=k+1)
			{
				if(j-a[i][k]*2-1>=0)
				{
					f[j]=max(f[j],f[j-a[i][k]*2-1]+k*i);
				}
			}
		}
	}
	int ans=0;
	for(int i=0;i<=m;i=i+1)
	{
		ans=max(ans,f[i]);
	}
	cout<<ans;
}

步骤

动归解题步骤:1.手动模拟样例,感受枚举过程 2.定义状态,推导递推方程 3.自底向上,使用dp数组循环 4.空间优化:滚动数组

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值