能量项链
这是一个很显然的区间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+u→v∑max(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)