5.24题单题解

能量项链

这是一个很显然的区间DP,对于这种环状问题,我们可以把环拆成链,设F[L][R]表示合并完L到R后的最大值。 状态转移: F[l][R]=max(F[L][mid]+F[mid+1][R]+该断点处的贡献,F[L][R]); 然后先枚举区间大小接着扫一遍就行了。

c++

#include<bits/stdc++.h>
using namespace std;
int F[201][201];
int N,ans;
int a[201];
int main()
{
    cin>>N;
    for (int i=1;i<=N;i++)
    {
        cin>>a[i];
        a[i+N]=a[i]; 
    }
    for (int i=2;i<2*N;i++)
      for (int l=1;l+i-1<2*N;l++)
      {
          int r=l+i-1;
          for (int k=l;k<r;k++)
            F[l][r]=max(F[l][r],F[l][k]+F[k+1][r]+a[l]*a[k+1]*a[r+1]);
      }
    for (int i=1;i<=N;i++)
      ans=max(ans,F[i][i+N-1]);
    cout<<ans;
    return 0;
}

java

import java.util.Scanner;

public class MatrixChainMultiplication {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int[][] F = new int[201][201];
        int N = sc.nextInt();
        int ans = 0;
        int[] a = new int[201];

        for (int i = 1; i <= N; i++) {
            a[i] = sc.nextInt();
            a[i + N] = a[i];
        }

        for (int i = 2; i < 2 * N; i++) {
            for (int l = 1; l + i - 1 < 2 * N; l++) {
                int r = l + i - 1;
                for (int k = l; k < r; k++) {
                    F[l][r] = Math.max(F[l][r], F[l][k] + F[k + 1][r] + a[l] * a[k + 1] * a[r + 1]);
                }
            }
        }

        for (int i = 1; i <= N; i++) {
            ans = Math.max(ans, F[i][i + N - 1]);
        }

        System.out.println(ans);
    }
}

python(该python代码运行上有点小问题具体以c++为主)

import sys
import math
import os
F = [[0 for i in range(201)] for j in range(201)]
N = 0
ans = 0
a = [0 for i in range(201)]

def main():
    N = int(input())
    for i in range(1, N+1):
        a[i] = int(input())
        a[i+N] = a[i]
    
    for i in range(2, 2*N):
        for l in range(1, 2*N-i+1):
            r = l + i - 1
            for k in range(l, r):
                if F[l][k] + F[k+1][r] + a[l] * a[k+1] * a[r+1] > F[l][r]:
                    F[l][r] = F[l][k] + F[k+1][r] + a[l] * a[k+1] * a[r+1]
    
    for i in range(1, N+1):
        if F[i][i+N-1] > ans:
            ans = F[i][i+N-1]
    
    print(ans)

if __name__ == "__main__":
    main()

组合数问题

对于组合数问题,我们可以考虑杨辉三角,可以发现 C[i][j]=c[i-1][j]+c[i-1][j-1] 其中 c[i][j]表示i个物品中选j个的方案数。这样我们就可以在N^2的时间里预处理出C[i][j]。

接着我们会发现要满足k|c[i][j] 只要满足c[i][j]%k==0就行了,这样我们在预处理的过程中边模边进行就可以避免爆long long且可以找出那些(i,j)满足条件。

接着我们来考虑答案 设状态方程F[n][m]表示在给定n,m下答案个数为f[n][m]

我们来考虑转移 f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+c[i][j]==0; f[i][j]可以从(i-1,j)和(i,j-1)转移过来,但要减去两者的公共部分(i-1,j-1),最后再考虑当前位置有没有贡献 即c[i][j]==0.

c++

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int t,k,n,m;
int c[2005][2005],s[2005][2005];
void prepare();
int main(){
    memset(c,0,sizeof(c));
    memset(s,0,sizeof(s));
    cin>>t>>k;
    prepare();
    while(t--){
        cin>>n>>m;
        if(m>n) m=n;
        cout<<s[n][m]<<endl;
    }    
    return 0;
} 
void prepare(){
    c[1][1]=1;
    for(int i=0;i<=2000;i++) c[i][0]=1;
    for(int i=2;i<=2000;i++){
        for(int j=1;j<=i;j++){
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;
        }
    }
    for(int i=2;i<=2000;i++){
        for(int j=1;j<=i;j++){
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
            if(c[i][j]==0) s[i][j]+=1;
        }
        s[i][i+1]=s[i][i];
    }
}

Java

import java.util.Scanner;
public class Main {
    static int t, k, n, m;
    static int[][] c = new int[2005][2005];
    static int[][] s = new int[2005][2005];
    public static void prepare() {
        c[1][1] = 1;
        for (int i = 0; i <= 2000; i++) c[i][0] = 1;
        for (int i = 2; i <= 2000; i++) {
            for (int j = 1; j <= i; j++) {
                c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % k;
            }
        }
        for (int i = 2; i <= 2000; i++) {
            for (int j = 1; j <= i; j++) {
                s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
                if (c[i][j] == 0) s[i][j] += 1;
            }
            s[i][i + 1] = s[i][i];
        }
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        t = scanner.nextInt();
        k = scanner.nextInt();
        prepare();
        while (t-- > 0) {
            n = scanner.nextInt();
            m = scanner.nextInt();
            if (m > n) m = n;
            System.out.println(s[n][m]);
        }
    }
}

Python

t = 0
k = 0
n = 0
m = 0
c = [[0 for i in range(2005)] for j in range(2005)]
s = [[0 for i in range(2005)] for j in range(2005)]

def prepare():
    c[1][1] = 1
    for i in range(2001):
        c[i][0] = 1
    for i in range(2, 2001):
        for j in range(1, i+1):
            c[i][j] = (c[i-1][j] + c[i-1][j-1]) % k
    for i in range(2, 2001):
        for j in range(1, i+1):
            s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1]
            if c[i][j] == 0:
                s[i][j] += 1
        s[i][i+1] = s[i][i]

if __name__ == "__main__":
    t, k = map(int, input().split())
    prepare()
    for _ in range(t):
        n, m = map(int, input().split())
        if m > n:
            m = n
        print(s[n][m])

生命之树

题意:对于一棵树,找到其点权和最大的一个连通分量(注意可以为空),输出这个连通分量的点权和。
我们用ai表示第 i个点的权值,dpu表示在以u为根的子树中最大的点权和,不妨设1号节点为这棵树的根,其父亲为0号节点
因为对于u的任意一个儿子v,如果dpu>0,那么以 u为根的点权和最大的子树一定要加上以v为根的点权和最大的子树,所以得到状态转移方程为:
d p u = a u + ∑ u → v m a x ( 0 , d q v ) dp_{u}=a_{u} + \sum_{u\to v }^{} max(0,dq_{v} ) dpu=au+uvmax(0,dqv)

c++

#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[900005];
int dp[900005];
struct lsqxx{int t,nxt;}e[900005];
int tot,num[900005];
void addedge(int f,int t){
	e[++tot].t=t;e[tot].nxt=num[f];num[f]=tot;}
void dfs(int p,int f){
	dp[p]=a[p];
	for(int i=num[p];i;i=e[i].nxt){
		if(e[i].t!=f){
			dfs(e[i].t,p);
			if(dp[e[i].t]>0)
				dp[p]+=dp[e[i].t];
		}
	}
}
signed main(){
	int n;cin>>n;
	for(int i=1;i<=n;i++)	cin>>a[i];
	for(int i=1;i<n;i++){
		int f,t;
		cin>>f>>t;
		addedge(f,t),addedge(t,f);
	}
	int ans=dp[1];
	dfs(1,0);
	for(int i=1;i<=n;i++)
		ans=max(ans,dp[i]);
	cout<<ans;
	return 0;
} 

java

import java.util.*;

public class Main {
    static long[] a = new long[900005];
    static long[] dp = new long[900005];
    static class Lsqxx {
        int t, nxt;
    }
    static Lsqxx[] e = new Lsqxx[900005];
    static int tot;
    static int[] num = new int[900005];

    static void addedge(int f, int t) {
        e[++tot] = new Lsqxx();
        e[tot].t = t;
        e[tot].nxt = num[f];
        num[f] = tot;
    }

    static void dfs(int p, int f) {
        dp[p] = a[p];
        for (int i = num[p]; i > 0; i = e[i].nxt) {
            if (e[i].t != f) {
                dfs(e[i].t, p);
                if (dp[e[i].t] > 0) {
                    dp[p] += dp[e[i].t];
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        for (int i = 1; i <= n; i++) {
            a[i] = scanner.nextLong();
        }
        for (int i = 1; i < n; i++) {
            int f = scanner.nextInt();
            int t = scanner.nextInt();
            addedge(f, t);
            addedge(t, f);
        }
        int ans = (int) dp[1];
        dfs(1, 0);
        for (int i = 1; i <= n; i++) {
            ans = Math.max(ans, (int) dp[i]);
        }
        System.out.println(ans);
    }
}

python

class lsqxx:
    def __init__(self):
        self.t = 0
        self.nxt = 0

a = [0] * 900005
dp = [0] * 900005
e = [lsqxx() for _ in range(900005)]
tot = 0
num = [0] * 900005

def addedge(f, t):
    global tot
    tot += 1
    e[tot].t = t
    e[tot].nxt = num[f]
    num[f] = tot

def dfs(p, f):
    dp[p] = a[p]
    i = num[p]
    while i != 0:
        if e[i].t != f:
            dfs(e[i].t, p)
            if dp[e[i].t] > 0:
                dp[p] += dp[e[i].t]
        i = e[i].nxt

if __name__ == "__main__":
    n = int(input())
    a[1:n+1] = list(map(int, input().split()))
    for _ in range(n-1):
        f, t = map(int, input().split())
        addedge(f, t)
        addedge(t, f)
    
    ans = dp[1]
    dfs(1, 0)
    
    for i in range(1, n+1):
        ans = max(ans, dp[i])
    
    print(ans)
  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值