Paintball(dfs判斷連通)

Question:題目詳情(http://vjudge.net/contest/134361#problem/C)
題目大意:有一個1000*1000的方陣(建立座標),這之間有一些士兵有具體的座標,並且具有一定的攻擊半徑,現你要從最左邊進入,途中避開所有的士兵(不被攻擊),從最右邊出,如果有多解則輸出離北邊最近的額座標
解題思路:可以把士兵看成一個個圓,看成池塘的石頭,如果能從池塘的最北邊走石頭走到最南邊則不可能避開所有的士兵,因爲左右兩邊不連通。如果不能從北邊走到南邊,從北邊出發找到與左邊相交的圓,取相連的圓與左邊邊界相交的最小值即爲起點,右邊雷同左邊,詳情見代碼

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1000+5;
struct node
{
    int x,y,r;
}a[maxn];
int vis[maxn],n;
double ans1,ans2;
bool judge(node b,node c)  //判斷兩個人是否相交
{
    return (b.x-c.x)*(b.x-c.x)+(b.y-c.y)*(b.y-c.y)<=(b.r+c.r)*(b.r+c.r);
}
bool dfs(int u)
{
    vis[u]=1;
    if(a[u].y-a[u].r<=0)   return false;  //如果既與北邊連通,又與南邊相交,則不可能成功
    if(a[u].x-a[u].r<=0)   ans1=min(ans1,a[u].y-sqrt(a[u].r*a[u].r-a[u].x*a[u].x));  //如果與左邊邊界相交,則取較小的一個交點
    if(a[u].x+a[u].r>=1000)  ans2=min(ans2,a[u].y-sqrt(a[u].r*a[u].r-(1000-a[u].x)*(1000-a[u].x)));  //如果與右邊邊界相連,則取交點最小的一個
    for(int i=0;i<n;i++)
    {
        if(!vis[i])  //節點沒有被訪問過
        {
            if(judge(a[u],a[i]))   //如果兩個圓相交,則繼續 
                if(!dfs(i))  return false ;  //如果相交的圓與最南邊相交則失敗
        }
    }
    return true;
}
void solve()
{
    for(int i;i<n;i++)
    {
        if(!vis[i]&&a[i].y+a[i].r>=1000)  //前提是從北邊出發,與北邊有交點
            if(!dfs(i))  {printf("IMPOSSIBLE\n");return ;}//如果左右不連通則說明不能成功
    }
    printf("0.00 %.2f 1000.00 %.2f\n",ans1,ans2);
}
int main()
{
    while(~scanf("%d",&n))
    {
        ans1=1000;
        ans2=1000;
        memset(vis,0,sizeof(vis));
        for(int i=0;i<n;i++)
            scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].r);
        solve();
    }
    return 0;
}

體會:開始沒想到,看了題解以後才豁然開朗,又是必須要轉化思路,簡化問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章