0
点赞
收藏
分享

微信扫一扫

HENAU冬令营-递推专题(第七组题解)

酷子腿长一米八 2022-02-10 阅读 38

A - 上台阶2

#include<iostream>
using namespace std;
long step[1000003] = {0};
int main(){
    int N;
    cin>>N;
    if(N<=0){
        return 0;
    }
    step[0] = 1;
    step[1] = 1;
    step[2] = 2;
    step[3] = 3;
    step[4] = 5;
    if(N<=5){
        cout<<step[N-1]<<endl;
        return 0;
    }
    for(int i=5;i<N;i++){
        step[i]= (step[i-1]+step[i-3]+step[i-5])%100003;
    }
    cout<<step[N-1]<<endl;
}

B - 数字三角形

#include<bits/stdc++.h>
using namespace std;

const int N=510;
int maxs[N][N];
int f[N][N];
int n;
void dp(){
    for(int i=1;i<=n;i++) maxs[n][i]=f[n][i];
    for(int i=n-1;i>=1;i--){
        for(int j=1;j<=i;j++){
            maxs[i][j]=max(maxs[i+1][j],maxs[i+1][j+1])+f[i][j];
        }
    }
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=i;j++){
            cin>>f[i][j];
        }
    }

    dp();
    cout<<maxs[1][1]<<endl;
}

C - 矩阵取数问题

#include<stdio.h>
#include<iostream>
using namespace std;
long long dp[510][510];
int main()
{
    int N,s[510][510];
    scanf("%d",&N);
    for(int i=1;i<=N;i++)
    {
        for(int j=1;j<=N;j++)
        {
            scanf("%d",&s[i][j]);
        }
    }
   /* for(int i=0;i<=N;i++)
    {
        for(int j=0;j<=N;j++)
        {
            dp[i][j]=-1;
        }
    }防止矩阵中有负数*/ 
    dp[1][1]=s[1][1];
    for(int i=1;i<=N;i++)
    {
        for(int j=1;j<=N;j++)
        {
            dp[i][j]=max(dp[i][j],max(dp[i-1][j],dp[i][j-1])+s[i][j]);
        }
    }
    printf("%lld\n",dp[N][N]);
    return 0;
}

D - 背包问题(01背包)

#include<bits/stdc++.h>

using namespace std;

const int MAXN = 10005;
int f[MAXN];  // 

int main() 
{
    int n, m;   
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        int v, w;
        cin >> v >> w;      // 边输入边处理
        for(int j = m; j >= v; j--)   //  v 体积  w  价值  m 最大容量
            f[j] = max(f[j], f[j - v] + w);
    }
    cout << f[m] << endl;

    return 0;
}

E - 完全背包

#include<iostream>
using namespace std;

const int N = 50010;

int n, m;
int dp[N];

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i ++ ){
        int v, w;
        cin >> v >> w;  // v 物品体积   w 价值
        for(int j = v; j <= m; j ++ ){ 
                dp[j] = max(dp[j], dp[j - v] + w);
        }
    }
    cout << dp[m] << endl;
}

F - 背包问题 V2 (多重背包)

//  在01背包基础上  多次判断此物品
#include <bits/stdc++.h>
using namespace std;
int dp[50015],n,t,v,w,s;
main()
{
    cin>>n>>t;
    while(n--)
    {
    cin>>w>>v>>s;   // w 体积  v 价值  s 数量
    for(int i=1;i<=s;i++)
    for(int j=t;j>=w;j--)    //t 最大容量
    dp[j]=max(dp[j],dp[j-w]+v);
    }
    cout<<dp[t];
}
#include <bits/stdc++.h>
using namespace std;
int a[50005],b[50005],t=0,n,m,dp[50015]={ },w,v,s;
int main()
{
    cin>>n>>m;
    while(n--)
    {
    cin>>v>>w>>s;
    while(s--)
    {a[++t]=v;     // a 体积   b  价值
    b[t]=w;}//死拆,把多重背包拆成01背包
    }
    for(int i=1;i<=t;i++)
    for(int j=m;j>=a[i];j--)
    dp[j]=max(dp[j-a[i]]+b[i],dp[j]);//直接套01背包的板子
    cout<<dp[m]<<endl;
    return 0;
}        

G - 最长上升子序列

#include <iostream>
using namespace std;
const int N = 1010;
int n;
int w[N], f[N];
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) 
    cin >> w[i];
    int mx = 1;    // 找出所计算的f[i]之中的最大值,边算边找
    for (int i = 0; i < n; i++) {
        f[i] = 1;    // 设f[i]默认为1,找不到前面数字小于自己的时候就为1
        for (int j = 0; j < i; j++) {
            if (w[i] > w[j]) f[i] = max(f[i], f[j] + 1);    // 前一个小于自己的数结尾的最大上升子序列加上自己,即+1
        }
        mx = max(mx, f[i]);
    }
    cout << mx << endl;
    return 0;
}

H - 最长公共子序列Lcs

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
char a[N],b[N];
int dp[N][N];
int main()
{
	cin>>a+1>>b+1;
	int la=strlen(a+1),lb=strlen(b+1);
	for(int i=1;i<=la;i++)
		for(int j=1;j<=lb;j++)
		{
			dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
			if(a[i]==b[j])dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
		}
	int pos1=la,pos2=lb;
	stack<char>st;
	while(pos1>0&&pos2>0)
	{
		if(a[pos1]==b[pos2])
		{
			st.push(a[pos1]);
			pos1--;
			pos2--;
		}
		else if(dp[pos1][pos2]==dp[pos1-1][pos2])
			pos1--;
		else 
			pos2--;
	}
	while(!st.empty())
	{
		cout<<st.top();
		st.pop();
	}
	return 0;
}

I - 石子合并
在这里插入图片描述

#include <iostream>
#include <cstring>

using namespace std;

const int N = 210, M = N << 1, INF = 0x3f3f3f3f;

int n;
int w[M], s[M];
int f[M][M], g[M][M];

int main()
{
    //读入
    scanf("%d", &n);
    for (int i = 1; i <= n; ++ i) cin >> w[i], w[i + n] = w[i];

    //预处理前缀和(DP状态转移中会频繁的用到区间和)
    for (int i = 1; i <= n << 1; ++ i) s[i] = s[i - 1] + w[i];

    memset(f, -0x3f, sizeof f);//求最大值预处理成最小值(可以省掉,这题不会有负数状态所以0就是最小)
    memset(g, +0x3f, sizeof g);//求最小值预处理成最大值(不可省掉)

    for (int len = 1; len <= n; ++ len)//阶段
    {
        for (int l = 1, r; r = l + len - 1, r < n << 1; ++ l)//左右区间参数
        {
            if (len == 1) f[l][l] = g[l][l] = 0;//预处理初始状态
            else
            {
                for (int k = l; k + 1 <= r; ++ k)//枚举分开点
                {
                    f[l][r] = max(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]),
                    g[l][r] = min(g[l][r], g[l][k] + g[k + 1][r] + s[r] - s[l - 1]);
                }
            }
        }
    }
    //目标状态中找出方案
    int minv = INF, maxv = -INF;
    for (int l = 1; l <= n; ++ l)
    {
        minv = min(minv, g[l][l + n - 1]);
        maxv = max(maxv, f[l][l + n - 1]);
    }

    //输出
    printf("%d\n%d\n", minv, maxv);

    return 0;
}

J - 循环数组最大子段和
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int a[N];
ll s,ans=-1e18,ans1=1e18,sum,s1;

int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		sum+=a[i];
	}
	for(int i=1;i<=n;i++)
	{
		if(s1>0)s1=0;
			if(s<0)s=0;  小于00即可
	   		s+=a[i];// 大于0 只管加即可  
	   		s1+=a[i];
			ans=max(ans,s);
			ans1=min(ans1,s1);// 计算中间某部分的最小和
	}
	ans=max(ans,sum-ans1);  //sum -中间最小和= 首尾处最大和
	cout<<ans;
}

K - 没有上司的舞会— 树形dp入门
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+10;
int happy[N];
bool has_fa[N];
int  dp[N][2];
int e[N],h[N],ne[N],idx;
vector<int>link[N];
queue<int>q;
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs(int u)
{
	dp[u][1]=happy[u];
	
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		dfs(j);
		dp[u][0]+=max(dp[j][0],dp[j][1]);
		dp[u][1]+=dp[j][0];
	}
}

int main()
{
	int n;
	cin>>n;
	
	memset(h,-1,sizeof h);
	for(int i=1;i<=n;i++)
		scanf("%d",&happy[i]);
	for(int i=1;i<n;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		link[a].push_back(b);
		link[b].push_back(a);
	}
	q.push(1);has_fa[1]=1;
	while(q.size())
	{
		int t=q.front();
		q.pop();
		for(int i=0;i<link[t].size();i++)
		{
			if(!has_fa[link[t][i]])
			{
				int tmp=link[t][i];
				q.push(tmp);
				has_fa[tmp]=1;
				add(t,tmp);
			}
		}
	}
	dfs(1);
	cout<<max(dp[1][0],dp[1][1])<<endl;
	return 0;
}

L - 滑雪
Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。
Input
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
Output
输出最长区域的长度。
Sample
Input
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
Output
25

include<bits/stdc++.h>
using namespace std;

const int N=110;

int dp[N][N],a[N][N];
int n,m,ans;

struct node
{
	int i,j,num;
	bool operator<(const node &t) const
	{
		return num>t.num;
	}
};

priority_queue<node>q; 
int main()
{
	cin>>n>>m;
	
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			cin>>a[i][j];
			q.push({i,j,a[i][j]});
			dp[i][j]=1;
		}
		
	while(q.size())
	{
		auto t=q.top();
		q.pop();
		int i=t.i,j=t.j;
		if(a[i-1][j]<a[i][j])dp[i][j]=max(dp[i-1][j]+1,dp[i][j]);
		if(a[i+1][j]<a[i][j])dp[i][j]=max(dp[i+1][j]+1,dp[i][j]);
		if(a[i][j-1]<a[i][j])dp[i][j]=max(dp[i][j-1]+1,dp[i][j]);
		if(a[i][j+1]<a[i][j])dp[i][j]=max(dp[i][j+1]+1,dp[i][j]);
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			ans=max(ans,dp[i][j]);
			cout<<ans<<endl;
}

举报

相关推荐

0 条评论