《浅谈数据的合理组织》笔记
来源:互联网 发布:qq自动群发软件 编辑:程序博客网 时间:2024/05/16 17:37
关键词:树结构->线性结构、倒序dp、背包问题初始化、第二类线段树
例一:(vjios1642) 树中每个点有一个权值,求不超过m点且满足性质P的最大权值点集,性质P:点集中任一点的父节点一定也在点集中。
做法:将树结构线性化。
dfs性质:dfs遍历后子树在一个以根为起始点的连续区间内
前根遍历,得到数组line[],记录元素i的子树区间结束点(即子树元素个数)sum[i],设dp[i][j]:数组中元素i-n中选择j个点的最大权值,则dp[i][j]=max{ dp[i+1][j-1]+cost[line[i]],dp[i+sum[line[i]]][j] }
为什么要倒序dp,原因:决策是选择i子树中若干元素/不选i所在子树中任何元素,决策的结果是数组下标变大,需要大下标的dp值作为依靠,因此遍历需要向前,故而倒序dp
最后注意:背包问题初始化:不超过m件物品,初始化为0;装满m件物品,只有dp[i][0]初始化为0,其他初始化为-INF。
#include<stdio.h>#include<iostream>#include<string.h>#include<algorithm>#include<map>#include<vector>#include<queue>#define ll long long#define sf scanf#define pf printf#define maxn 10000#define INF 0x3f3f3f3f#define mem(a,b) memset(a,b,sizeof(a))#define lowbit(x) x&(-x)const ll mod=1000000007;using namespace std;int n,m;struct Edge{ int to,next;}edge[maxn];int head[maxn],tot;int cost[maxn];int line[maxn],cnt,sum[maxn];int dp[maxn][maxn];void add(int u,int v){ edge[tot].to=v,edge[tot].next=head[u],head[u]=tot++;}void tree_line(int u){ line[cnt++]=u,sum[u]=1; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; tree_line(v); sum[u]+=sum[v]; }}int main(){ //freopen("a.txt","r",stdin); scanf("%d%d",&n,&m); mem(head,-1),tot=0,cnt=0,mem(sum,0),mem(cost,0); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) dp[i][j]=0;//初始化---不装满:初始化为0 for(int i=1;i<=n;i++){ int s,son; scanf("%d%d",&cost[i],&s); for(int j=1;j<=s;j++){ scanf("%d",&son); add(i,son); } } tree_line(1); for(int i=cnt-1;i>=0;i--){//dp[i][j]=max{dp[i+sum[line[i]]][j],dp[i+1][j-1]+cost[list[i]]} for(int j=1;j<=m;j++){ dp[i][j]=max(dp[i+sum[line[i]]][j],dp[i+1][j-1]+cost[line[i]]); } } int ans=dp[0][m]; printf("%d\n",ans); return 0;}
例二:有根树,对每个节点,求:1..它到根节点路径上比它权值大的节点数目 2.其子树上比它权值大的节点数目
1.从根节点开始遍历,第一次经过节点时加入该节点,遍历完成后去除该节点,即可得到根节点到达每一节点的路径。则题目转化为求第i个元素之前比i大的元素个数,于是可以使用树状数组进行统计。
#include<stdio.h>#include<iostream>#include<string.h>#include<algorithm>#include<map>#include<vector>#include<queue>#define ll long long#define sf scanf#define pf printf#define maxn 10000#define INF 0x3f3f3f3f#define mem(a,b) memset(a,b,sizeof(a))#define lowbit(x) x&(-x)const ll mod=1000000007;using namespace std;int n,cost[maxn],cost1[maxn];int c[maxn],ans[maxn];vector<int> g[maxn];void seperate(int h[],int h1[]){//数据过大时,可以先离散化 int h0[maxn]; for(int i=1;i<=n;i++) h0[i]=h[i]; sort(h0+1,h0+n+1); for(int i=1;i<=n;i++) h1[i]=lower_bound(h0+1,h0+n+1,h[i])-h0;}void build_map(){ for(int i=1;i<=n;i++) g[i].clear(); for(int i=1;i<n;i++){ int a,b; scanf("%d%d",&a,&b); g[a].push_back(b); }}int sum(int i){ int s=0; for(;i>0;i-=lowbit(i)) s+=c[i]; return s;}void update(int i,int x){ for(;i<=maxn;i+=lowbit(i)) c[i]+=x;}void dfs(int u){ ans[u]=sum(maxn)-sum(cost1[u]);//根节点到u的路径上比u大的元素个数 update(cost1[u],1); for(int i=0;i<g[u].size();i++){ int v=g[u][i]; dfs(v); } update(u,-1);}int main(){ //freopen("a.txt","r",stdin); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&cost[i]); seperate(cost,cost1); build_map(); mem(c,0); dfs(1); for(int i=1;i<=n;i++) printf("%d ",ans[i]); printf("\n");}
2.统计子树中比该子树根节点大的元素的个数
首先,和例一一样,先把树结构转化为数组。问题转化为:
一个静态序列a[1..n],询问n组子区间中比所在子区间起始位置元素大的元素个数
做法:第二种统计区间元素比x大/小的元素个数方法。1.将所有数从大到小排序,记录在原数组中的下标位置(结构体存储) 2.将这些数从大到小依次插入到原数组中的相应位置,标记为1,插入前统计以该点起始的子树区间的和---即为该子树中比子树根节点元素大的个数。
第一种线段树方法是以值为底建立线段树,若值较大时,需要对相应值进行离散化;第二种仍以下标为底建立线段树,只不过需要先排序,按从大到小的顺序插入。可以解决"任意区间的统计较大元素的个数"问题
#include<stdio.h>#include<iostream>#include<string.h>#include<algorithm>#include<map>#include<vector>#include<queue>#define ll long long#define sf scanf#define pf printf#define maxn 10000#define INF 0x3f3f3f3f#define mem(a,b) memset(a,b,sizeof(a))#define lowbit(x) x&(-x)const ll mod=1000000007;using namespace std;int n,cost[maxn],cost1[maxn];int sum[maxn],line[maxn],cnt;int c[maxn],ans[maxn];vector<int> g[maxn];struct node{ int id,cost; bool operator <(const node &rhs)const{ return cost>rhs.cost; }}vertex[maxn];void seperate(int h[],int h1[]){ int h0[maxn]; for(int i=1;i<=n;i++) h0[i]=h[i]; sort(h0+1,h0+n+1); for(int i=1;i<=n;i++) h1[i]=lower_bound(h0+1,h0+n+1,h[i])-h0;}void build_map(){ for(int i=1;i<=n;i++) g[i].clear(); for(int i=1;i<n;i++){ int a,b; scanf("%d%d",&a,&b); g[a].push_back(b); }}void tree_line(int u){ sum[u]=1,line[cnt]=u,num[u]=cnt++;//cnt:0...n-1 for(int i=0;i<g[u].size();i++){ int v=g[u][i]; tree_line(v); sum[u]+=sum[v]; }}int cal(int i){ int s=0; for(;i>0;i-=lowbit(i)) s+=c[i]; return s;}void update(int i,int x){ for(;i<=maxn;i+=lowbit(i)) c[i]+=x;}int main(){ //freopen("a.txt","r",stdin); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&cost[i]); seperate(cost,cost1); build_map(); cnt=1,mem(c,0),mem(num,0); tree_line(1);
//转化为解决line数组中任意区间的较大元素统计问题 for(int i=1;i<=n;i++) { vertex[i].cost=cost1[line[i]],vertex[i].id=i; }//id是在line数组中的编号 sort(vertex+1,vertex+n+1); for(int i=1;i<=n;i++){ ans[line[vertex[i].id]]=cal(vertex[i].id+sum[line[vertex[i].id]]-1)-cal(vertex[i].id); update(vertex[i].id,1); } for(int i=1;i<=n;i++) printf("%d ",ans[i]); printf("\n");}
例三:航线规划
不断添边并询问任意两点间桥的数量
思路:1.缩点,建立edge_bcc图 2.添边后,缩点,更新图和深度(如何实现???代码能力不足Orz...)
- 《浅谈数据的合理组织》笔记
- 项目型公司合理的组织结构
- 合理的组织电脑目录结构
- 一些思考:嵌入式软件设计公司合理的组织结构
- 切图崽的自我修养-如何合理组织CSS
- SuperMap的数据组织
- 关系数据的组织
- 汇编--学习笔记(五)-组织数据
- 通过工具—把思想有逻辑、有细节的合理的组织到一起!
- 使用C#调用存储过程,用函数合理组织代码,使程序更加的清晰(示例)
- java后台的数据层数据组织
- inder学习笔记(三)—— binder客户端是如何组织checkService数据的 ?
- Binder学习笔记(六)—— binder服务端是如何组织addService数据的?
- caffe的训练之一,数据的组织。
- 数据仓库的数据模型与数据组织
- 基于网格的空间数据组织
- 3D游戏场景数据的组织
- HTML&CSS 表格:组织数据的工具
- JVM简介
- 常用shell脚本操作
- 超过130个你需要了解的vim命令
- dreamweaver快捷键冲突修改emmet
- 在AppServ上搭建WordPress环境
- 《浅谈数据的合理组织》笔记
- hdu2807---The Shortest Path
- FPGA机器学习之数据挖掘,图像处理,机器视觉,模式识别,人工智能,机器学习的关系
- 生成证书请求CSR
- mongodb安装与验证
- 用CA给证书签名
- Linux下共享内存示例
- 1.ARM寄存器简解
- WordPress做微信公众平台