把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF698D】Limak and Shooting Points(DFS)

点此看题面

  • 平面上有\(k\)个人和\(n\)个怪物,规定一个人一次只能向某个方向射死第一个怪物。
  • 求有多少怪物可能被射死。
  • \(k\le 7,n\le10^3\)

暴力全排列+顺序指定

对于一个怪物,我们直接枚举人的全排列。

我们假设由第一个人射死了这个怪物,那么之后的人就要为他扫清障碍,方便起见规定之后的人从第一个人到这个怪物之间的第一个怪物开始依次扫清。

然而,第二个人若想扫清第一个障碍,中间可能还会存在其他障碍,这相当于是一个递归的过程,就由第三个人开始为他先扫清障碍。

具体实现中只要定义一个\(Check(x)\)函数,并在此之外设立一个全局变量\(nw\)表示当前要让\(nw\)射死\(x\),然后递归调用就可以解决了。

因为我们枚举了全排列,所以这样强定顺序是毫无问题的。

最好现在一开始预处理出每个人和每个怪物之间所有的障碍怪物,能够提高代码效率。

代码:\(O(kn^2+nk\cdot k!)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define K 7
#define N 1000
using namespace std;
int k,n,id[K+5];vector<int> P[K+5][N+5];
I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
struct P
{
	int x,y;I P(CI a=0,CI b=0):x(a),y(b){}
	I long long L2() {return 1LL*x*x+1LL*y*y;}
	I P S() {RI d=gcd(abs(x),abs(y))*(x?(x>0?1:-1):(y>0?1:-1));return P(x/d,y/d);}
	I P operator - (Con P& o) Con {return P(x-o.x,y-o.y);}
	I bool operator < (Con P& o) Con {return x^o.x?x<o.x:y<o.y;}
	I bool operator == (Con P& o) Con {return x==o.x&&y==o.y;}
}s[K+5],p[N+5];
int nw,vis[N+5];I bool Check(CI x)//检验nw能否射死x
{
	if(nw>k) return 0;RI o=id[nw];vector<int>::iterator it;
	for(it=P[o][x].begin();it!=P[o][x].end();++it) if(!vis[*it]&&(++nw,!Check(*it))) return 0;//把中间的障碍全部清除
	return vis[x]=1;//标记被杀死
}
int op;I bool cmp(CI x,CI y) {return (p[x]<p[y])==op;}
int main()
{
	RI i,j,u;for(scanf("%d%d",&k,&n),i=1;i<=k;++i) scanf("%d%d",&s[i].x,&s[i].y);
	for(i=1;i<=n;++i) scanf("%d%d",&p[i].x,&p[i].y);
	for(i=1;i<=k;++i) for(j=1;j<=n;op=s[i]<p[j],sort(P[i][j].begin(),P[i][j].end(),cmp),++j)//枚举人i和怪物j,求出之间的怪物
		for(u=1;u<=n;++u) j^u&&(s[i]-p[j]).S()==(s[i]-p[u]).S()&&(s[i]<p[u])==(p[u]<p[j])&&(P[i][j].push_back(u),0);//枚举怪物判断是否在它们中间
	RI fg,t=0;for(i=1;i<=n;t+=fg,++i) {for(j=1;j<=k;++j) id[j]=j;
		do {memset(vis,0,sizeof(vis)),nw=1,fg=Check(i);} W(!fg&&next_permutation(id+1,id+k+1));}//暴力全排列
	return printf("%d\n",t),0;
}
posted @ 2021-04-06 07:03  TheLostWeak  阅读(56)  评论(0编辑  收藏  举报