20201024 day44 复习12:动态规划完全复习

1 [P1115]最大子段和

solution

dp[i]=max{sum[i]-max{sum[j]}}
一个\(O(n)\)的优秀做法

2 [P1855]榨取kkksc03

solution

dp[i][j]=max(dp[i][j],dp[i-m[k]][j-t[k]]
一个多维的背包。

3 [P1832]A+B Problem升级'

solution

一个完全背包。
dp[i]=dp[i]+dp[i-prime[j]]

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

solution

a[i][j]+=max(a[i-1][j-1],a[i-1][j]),ans=max(ans,a[i][j]);

5 [P1470][USACO2.3]最长前缀 Longest Prefix

solution

从末尾开始截取子串,set可以去重排序。

code

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <set>
#include <iostream>
using namespace std;
int read(){
	int op=1,a=0;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') op=-1;c=getchar();}
	while(c>='0'&&c<='9'){a*=10,a+=c^48,c=getchar();}
	return a*op;	
}
const int maxn=2e5+5;
int m,dp[maxn],ans;
set<string> s[25];
int main(){
	string tp;
	while(cin>>tp){
		if(tp==".") break;
		s[tp.size()].insert(tp);
		m=max(m,int(tp.size()));	
	}
	dp[0]=1;
	string n;
	n=" ";
	while(cin>>tp) n=n+tp;
	for(int i=1;i<n.size();i++){
		for(int j=min(i,m);j>=1;j--){//m是最长子串的长度
			string tt=n.substr(i-j+1,j);//截取
			if(s[tt.size()].count(tt)==1&&dp[i-j]==1)
				{ans=i,dp[i]=1;break; }
		}	
	}
	printf("%d",ans);
	return 0;
}

6 [P2642]双子序列最大和

solution

分别从前和从后求,再枚举每个位置。\(O(n)\)的巧妙做法。

code

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
long long read(){
	long long x=1,a=0;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') x=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){a=a*10+ch-'0';ch=getchar();}
	return x*a;
}
const int maxn=1e6+10;
long long a[maxn],f[maxn],l[maxn];
long long n;
int main(){
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	f[1]=a[1];
	for(int i=2;i<=n;i++) f[i]=max(f[i-1],0ll)+a[i];
	for(int i=2;i<=n;i++) f[i]=max(f[i-1],f[i]);
	l[n]=a[n];
	for(int i=n-1;i>=1;i--) l[i]=max(l[i+1],0ll)+a[i];
	for(int i=n-1;i>=1;i--) l[i]=max(l[i+1],l[i]);
	long long ans=f[1]+l[3];
	for(int i=3;i<n;i++) ans=max(ans,f[i-1]+l[i+1]);
	printf("%lld",ans);
	return 0;
}

7 [P2782][NOIp2001]友好城市

solution

最长不下降。使用lower_bound降低复杂度。

8 [P1439]【模版】最长公共子序列

solution

使用map数组来映射位置。

code

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

inline ll read() {
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
int main() {
    ll ans, m;
    for (int i = 1; i <= 3000000; i++) {
        m = read();
        ans ^= m;
    }
    printf("%lld", ans);
    return 0;
}

9 换根dp

solution

我们可以利用一些技巧来把这一类问题优化到 \(\Theta(n)\)的时间内解决

接下来,我们就要引入换根dp的一些思想 (或者叫套路) 了

换根dp一般分为三个步骤

1、先指定一个根节点
2、一次dfs统计子树内的节点对当前节点的贡献
3、一次dfs统计父亲节点对当前节点的贡献并合并统计最终答案

光这么说可能不太好理解,我们来结合上面的那个例子看看吧

我们先来看一个节点\(u\)的子树里面的节点对它的贡献:

很明显,这个贡献就是子树里所有节点到\(u\)的深度和,我们把它记为 \(g_u\) (不过待会会发现根本不需要这个 gug_ugu​)

接着,我们记 \(sz_u\)​ 为以 \(u\) 为根的子树大小,\(dep_u\)为点\(u\) 到 1 号节点(我们指定的根节点)的深度(之后有用)

接下来,我们来考虑第2次dfs,也就是计算父亲对它的贡献

我们令 \(f_u\)​ 为以 \(u\) 为根节点的深度和

所以,我们令 \(v\)\(u\)的一个儿子节点,可得\(f_v=g_v+(f_u-(g_v+sz_v))+(sz_1-sz_v)\)

为啥打上了括号呢?是因为这样更好理解了

如果看不懂,我来解释一下

\(g_v\) 是以\(v\)为根的子树深度和,显然要加上
fuf_ufu​ 是父亲 uuu 节点的答案,显然要减去我们 vvv 子树里的信息(不然就多算了)
那么, \(g_v+sz_v\)就是我们\(v\)子树的信息了

有人就要问了,子树里的的信息不就是\(g_v\)吗?

但是我们是从父亲的答案减去,显然在以 \(u\) 为根时, \(v\) 的子树中的所有节点的深度都有加一,于是就增加 \(sz_v\)

同理,后面的\(sz_1-sz_v\)​ 就是非 \(v\) 节点子树中的点啦,和上面一样理解一下就好啦qwq

解释完了,我们化简一下这个柿子 \(f_v=f_u+sz_1-2\times sz_v\)

发现可以不用记录 \(g\)

code

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <vector>
#include <bitset>
using namespace std;
long long read(){
	long long op=1,a=0;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') op=-1;c=getchar();}
	while(c>='0'&&c<='9'){a*=10,a+=c^48,c=getchar();}
	return a*op;	
}
const int maxn=1e6+100;
struct node{
	int to,nxt;
}e[maxn<<1];
long long n,cnt,id,ans;
long long head[maxn],f[maxn],dep[maxn],siz[maxn];
inline void add(int u,int v){
	e[++cnt].nxt=head[u];
	head[u]=cnt;
	e[cnt].to=v;
}
void dfs1(int x,int fa){
	siz[x]=1;dep[x]=dep[fa]+1;
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(y==fa) continue;
		dfs1(y,x);
		siz[x]+=siz[y];
	}
}
void dfs2(int x,int fa){
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].to;
		if(y==fa) continue;
		f[y]=f[x]+n-2*siz[y];
		dfs2(y,x);
	}
}
int main(){
	n=read();
	for(int i=1;i<n;i++){
		int u,v;u=read(),v=read();
		add(u,v),add(v,u);
	}
	dfs1(1,0);
	for(int i=1;i<=n;i++) f[1]+=dep[i];
	dfs2(1,0);
	for(int i=1;i<=n;i++) if(ans<f[i]) ans=f[i],id=i;
	printf("%lld",id);
	return 0;
}
posted @ 2020-10-24 10:57  刘子闻  阅读(88)  评论(0编辑  收藏  举报