To_Heart—题解——CF1209F
题目链接
题解
可以发现,如果每一条边的编号位数相同就会很好处理,又可以发现走过一条编号为 1 的边和一条编号为 2 的边的贡献与走过一条编号为 12 的边相等,所以我们考虑拆边,将边按照十进制进行拆分。因为每条边只有一位了,所以最短的距离肯定走最少的边,所以我们把问题转换成了最短路问题。
然后因为如果存在多条出边,我们要走权最小的那条边,所以可以将出边进行 0~9 分类。
然还有一个问题,上文提到过走 1 再走 2 和走 12 是等价的,但如果出现下图的情况:
从
s
s
s 出发,我们有可能会选择走 1->2->3->4 的道路到达
t
t
t ,但显然这不是最近的道路。
为了避免这种情况,我们需要在每次搜索时让层数相同的一起扩展。
但还有一个问题:
如图,将第三层的放在一起扩展,但可能会出现
2
<
3
2< 3
2<3 ,所以我先从下面扩展的终点的情况。所以其实我们扩展并不是将同一层的放一起,而是将目前为止价值相同的按照下一条边的权值从小到大进行扩展。
code
#include<bits/stdc++.h>
using namespace std;
#define db double
int n,k,ans=0;
struct zz{
int x,y;
}a[15],b[1005];
vector<int> v[15][1005];
bool cmp(zz x,zz y){
if(x.x==y.x) return x.y<y.y;
return x.x<y.x;
}
bool Cheak(zz x,zz y,zz yy){
if(x.x==y.x&&x.x==yy.x) return 1;
if(x.x==y.x&&x.x==yy.x) return 1;
return (x.y-y.y)*(x.x-yy.x)==(x.y-yy.y)*(x.x-y.x);
}
void Fk(){
for(int i=1;i<=k;i++) for(int j=1;j<=n;j++) for(int l=1;l<=n;l++) if(j!=l&&Cheak(a[i],b[j],b[l]))
if(min(a[i].x,b[j].x)<=b[l].x&&b[l].x<=max(a[i].x,b[j].x)&&min(a[i].y,b[j].y)<=b[l].y&&b[l].y<=max(a[i].y,b[j].y))
v[i][j].push_back(l);
}
bool f[15];
int t[15],sum=0;
bool vis[1005];
bool DFS(int x){
if(sum==k+1) return 0;
int now=t[sum++];
for(auto y:v[now][x]) if(!vis[y]&&!DFS(y)) return 0;
vis[x]=1;
return 1;
}
bool Check(int x,int tot){
if(tot==k+1){ memset(vis,0,sizeof vis);sum=1;return DFS(x); }
for(int i=1;i<=k;i++) if(!f[i]){
f[i]=1;t[tot]=i;
if(Check(x,tot+1)) return 1;
f[i]=0;
}
return 0;
}
int main(){
cin>>k>>n;
for(int i=1;i<=k;i++) scanf("%d%d",&a[i].x,&a[i].y);
for(int i=1;i<=n;i++) scanf("%d%d",&b[i].x,&b[i].y);
Fk();
for(int i=1;i<=n;i++){
ans+=Check(i,1);
memset(f,0,sizeof f);
}
cout<<ans<<endl;
return 0;
}
```