• 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 imes 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;
    }
    
  • 相关阅读:
    简单了解一下:var 、let、const
    C# FlagAttriute 的 小妙招
    项目经验面试题
    linux面试题详解
    jvm面试题详解
    数据库面试详解
    微服务框架面试题
    框架面试题(maven、ZooKeeper、Dubbo、Nginx、Redis、Lucene、Solr、ActiveMQ、JMS
    设计模式面试题详解
    WEB方面面试题详解
  • 原文地址:https://www.cnblogs.com/liuziwen0224/p/20201024day44-001.html
Copyright © 2020-2023  润新知