洛谷 P4005 小 Y 和地铁 题解
Reference
题目入口:传送门。
本文在写作时有参考 这篇题解 ,在此表示感谢。
前置知识
- 树状数组
- 大法师(DFS)
Problem
简化版题意:给定n个点,其中每个点可能对应另外一个点。如果一个点有对应点,那么就要用曲线连接这两个点。这些曲线会有许多交点(不存在环、三线共点、交叉但没有交点这三种情况),求交点最少个数。
Solution
根据数据范围可以看出是二进制暴力搜索,看题解里 dalao 都用的是模拟退火云云,但是我不会,所以······
大法师+剪枝!~
优化1
不难发现,两个点最多有6种连线方式,时间复杂度O(6(n/2))。
我们发现,如果把2、3画在一起,他们一定是一个圆环,且包住了整条线段。假如有一条新的线段,则那条线段要么和2、3都没交点,要么都有交点。也就是说对于两个点,用2还是用3对答案的贡献都是一样的。
同理,4、5也一样。
这样就变成了4种。
优化2
我们试着将1和2画在一起,又是一个环,但这个环有点特殊,对在这两个点左边的点右侧的线段影响相同(在将所有线段排序后进行 DFS ),但左侧的点对于1或2的答案贡献是不同的。
所以,我们只需在 DFS 时,计算1、2的产生的答案贡献哪个更少,4、6同理。
优化3
在前两个优化的基础上加入树状数组,复杂度O(2(n/2))。
Code
//P4005 小 Y 和地铁 #include<iostream> #include<cstdio> #include<iomanip> #include<cmath> #include<cstring> #include<string> #include<cstdlib> #include<cctype> #include<algorithm> #include<sstream> #include<map> #include<list> //#include<windows.h> #include<stack> #include<queue> #include<utility> #include<climits> #include<vector> namespace TREEARR//树状数组模板 { inline int lowbit(int x) { return x&-x; } struct Treearr { int c[1000005],siz; void init(int p) { for(int i=1;i<=p;i++) { this->c[i]=0; } this->siz=p; return; } void add(int x,int d) { while(x<=siz) { this->c[x]+=d; x+=lowbit(x); } return; } int sum(int x) { int res=0; while(x) { res+=this->c[x]; x-=lowbit(x); } return res; } int query(int l,int r) { return this->sum(r)-this->sum(l-1); } }; } using namespace std; int t,n,a[45],pos,l[45],r[45],ans; TREEARR::Treearr up,down; void work_dfs(int st,int sum)//DFS { int a1,a2; if(st>pos) //找到可行解 { ans=min(ans,sum);//取最小值 return; } if(sum>ans)//一处小剪枝,当sum>ans时,即使有可行解也找不到比ans更优的,所以直接返回 { return; } a1=min(up.query(l[st],r[st]),down.query(l[st],n)+up.query(r[st],n));//第一种情况 up.add(r[st],1); work_dfs(st+1,sum+a1); up.add(r[st],-1); a2=min(down.query(l[st],r[st]),up.query(l[st],n)+down.query(r[st],n));//第二种情况 down.add(r[st],1); work_dfs(st+1,sum+a2); down.add(r[st],-1); return; } int main() { scanf("%d",&t); while(t--) { scanf("%d",&n); ans=1<<30;//注意每次的初始化 for(int j=1;j<=n;j++) { scanf("%d",a+j);//等同于scanf("%d",&a[j]); } pos=0; for(int j=1;j<=n;j++) { for(int k=j+2;k<=n;k++) { if(a[k]==a[j]) { l[++pos]=j; r[pos]=k; break; } } } up.init(n); down.init(n); work_dfs(1,0); printf("%d\n",ans); } return 0; }
本文作者:Day_Dreamer_D
本文链接:https://www.cnblogs.com/2020gyk080/p/luogu_P4005.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步