HDU 4183 Pahom on Water(点双连通分量)

题意:有很多的被涂上颜色的呈圆形的垫子,一些垫子可以有相同的颜色,但是红色和紫罗兰只有一个,垫子摆放的时候可以相交,初始一个人站在红垫子上,这个人要从红垫子走到紫罗兰垫子,然后再从紫罗兰垫子走到红垫子,问你是否可以在满足以下条件完成上述的动作。

      ~rule1(red->violet):a垫子和b垫子相交(要有公共面积,点不算)P(a)<P(b);则可以从a走到b;

      ~rule2(violet->red):a垫子和b垫子相交(要有公共面积,点不算)P(a)>P(b);则可以从a走到b;

思路:

点双连通分量:分量内的任意一个点到其中的其它各点都有两条没有公共点的路径相通。

建图的时候不一定所有满足条件的边都要建,只要建与red_pad和violet_pad有关的边即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
const int pad_num=300+5;
const int edge=305*305;
int n;
int red_id,vio_id;
struct node
{
	double fre;
	int x,y;
	int rad;
}pad[pad_num];
struct nodee
{
	int v,next;
}e[edge];
int head[pad_num],cnt;
int vis[pad_num];
void Init()
{
	memset(head,-1,sizeof(head));
	cnt=0;
}
void add(int a,int b)
{
	e[cnt].v=b;
	e[cnt].next=head[a];
	head[a]=cnt++;
}
int Input()
{
	int red=0,vio=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lf %d %d %d",&pad[i].fre,&pad[i].x,&pad[i].y,&pad[i].rad);
		if(abs(pad[i].fre-400)<1e-8) 
		{
	    	red_id=i;red=1;
		}
		if(abs(pad[i].fre-789)<1e-8) 
		{
			vio_id=i;vio=1;
		}
	}
	if(red==0||vio==0) return 0;
	return 1;
}
double Dis(int x1,int y1,int x2,int y2)
{
	return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
void dfs(int i)  
{  
    vis[i]=1;  
    for(int j=1;j<=n;++j) {  
        if(i==j) continue;  
        if(Dis(pad[i].x,pad[i].y,pad[j].x,pad[j].y)>(pad[i].rad+pad[j].rad)) continue;  
        if(fabs(pad[j].fre-pad[i].fre)<1e-8) continue;  
        if(pad[j].fre<pad[i].fre) continue;   
        add(i,j);
        add(j,i); 
        if(!vis[j]) dfs(j);  
    }  
}  
void buildmap()
{
	memset(vis,0,sizeof vis);
	dfs(red_id);
}
struct nnode
{
	int f,t;
}ee[pad_num];
int paint[pad_num],dfn[pad_num],low[pad_num],index,col;
stack<nnode>s;
void Init_tarjan()
{
	memset(paint,0,sizeof(paint));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	index=col=1;
	while(!s.empty()) s.pop();
}
int Min(int a,int b)
{
	if(a<b) return a;
	return b;
}
void tarjan(int u,int f)
{
	dfn[u]=low[u]=index++;
	for(int i=head[u];i+1;i=e[i].next)
	{
		int v=e[i].v;
		nnode w;
		w.f=u;w.t=v;
		if(!dfn[v])
		{
			s.push(w);
			tarjan(v,u);
			low[u]=Min(low[u],low[v]);
			if(low[v]>=dfn[u])
			{
				while(1)
				{
					nnode w=s.top();
					s.pop();
					paint[w.f]=col;
					paint[w.t]=col;
					if(w.f==u) break;
				}
				col++;
			}
		}
		else if(v!=f&&dfn[u]>dfn[v])
		{
			low[u]=Min(low[u],dfn[v]);
		}
	}
}
void treatment()
{
	Init_tarjan();
	for(int i=1;i<=n;i++)
	{
		if(!dfn[i]) tarjan(i,-1);
	}
	if(paint[red_id]==paint[vio_id]&&paint[red_id]!=0)
    printf("Game is VALID\n");
    else printf("Game is NOT VALID\n");
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		Init();
		if(!Input()) 
		{
			printf("Game is NOT VALID\n");
			continue;
		}
		buildmap();
		treatment();
	}
	return 0;
} 



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值