此后如竟没有炬火 我便是唯一的光|

Day_Dreamer_D

园龄:3年6个月粉丝:3关注:16

2021-10-30 21:29阅读: 80评论: 1推荐: 0

洛谷 P4005 小 Y 和地铁 题解

Reference

题目入口:传送门

本文在写作时有参考 这篇题解 ,在此表示感谢。

前置知识

  1. 树状数组
  2. 大法师(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 中国大陆许可协议进行许可。

posted @   Day_Dreamer_D  阅读(80)  评论(1编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起