HDU 2389 Rain on your Parade

Rain on your Parade



Problem Description
You’re giving a party in the garden of your villa by the sea. The party is a huge success, and everyone is here. It’s a warm, sunny evening, and a soothing wind sends fresh, salty air from the sea. The evening is progressing just as you had imagined. It could be the perfect end of a beautiful day.
But nothing ever is perfect. One of your guests works in weather forecasting. He suddenly yells, “I know that breeze! It means its going to rain heavily in just a few minutes!” Your guests all wear their best dresses and really would not like to get wet, hence they stand terrified when hearing the bad news.
You have prepared a few umbrellas which can protect a few of your guests. The umbrellas are small, and since your guests are all slightly snobbish, no guest will share an umbrella with other guests. The umbrellas are spread across your (gigantic) garden, just like your guests. To complicate matters even more, some of your guests can’t run as fast as the others.
Can you help your guests so that as many as possible find an umbrella before it starts to pour? 

Given the positions and speeds of all your guests, the positions of the umbrellas, and the time until it starts to rain, find out how many of your guests can at most reach an umbrella. Two guests do not want to share an umbrella, however. 
 

Input
The input starts with a line containing a single integer, the number of test cases.
Each test case starts with a line containing the time t in minutes until it will start to rain (1 <=t <= 5). The next line contains the number of guests m (1 <= m <= 3000), followed by m lines containing x- and y-coordinates as well as the speed si in units per minute (1 <= s i <= 3000) of the guest as integers, separated by spaces. After the guests, a single line contains n (1 <= n <= 3000), the number of umbrellas, followed by n lines containing the integer coordinates of each umbrella, separated by a space.
The absolute value of all coordinates is less than 10000.
 

Output
For each test case, write a line containing “Scenario #i:”, where i is the number of the test case starting at 1. Then, write a single line that contains the number of guests that can at most reach an umbrella before it starts to rain. Terminate every test case with a blank line.
 

Sample Input
  
  
2 1 2 1 0 3 3 0 3 2 4 0 6 0 1 2 1 1 2 3 3 2 2 2 2 4 4
 

Sample Output
  
  
Scenario #1: 2 Scenario #2: 2
 
 二分图问题,以宾客在时间T内能否跑到雨伞处作为二分条件,这里要用Hopcroft-Karp算法,匈牙利算法会T,这里给出C++ 和 Java实现,其中HK算法在代码中有详细注释和说明,另外用Java的System.out.println()函数会各种PE,希望有大神指点迷津。

C++版本:

/***************************************************************************
 *   二分图 Hopcroft-Karp 算法
 *   例HDU 2389
 *   复杂度 O(V^0.5 * E)
 *   算法思想:用BFS每次找路径长度为
 *   dis的增广路集合,再用dfs去找匹配,可通过MaxMatch()求得最大匹配
 *   1.总是搜索最短的增广路
 *   2.每轮搜索多条无公共顶点的增广路,全部替换
 *
 *   每轮搜索的步骤:
 *   1. 最短增广路所有可能的起点?
 *   – 所有未饱和的左侧顶点
 *   2. 最短增广路的长度及所有可能的终点?
 *   – 利用“并发的”广度优先搜索,对所有顶点进行分层(dx, dy即记录层次序号)
 *   – 上述所有可能的起点构成第0层
 *   – 与第i层相邻的所有未分层顶点构成第i+1层
 *   • i为偶数时,只能通过不在当前匹配中的边关联
 *   • i为奇数时,只能通过当前匹配中的边关联
 *   – 搜索终止于第k层的条件:第k层包含未饱和的右侧顶点,或已搜完所有顶点
 *   3. 如何找到多条无公共顶点的最短增广路,
 *   从而全部替换得到更大的匹配?
 *   – 在分层信息的引导下,利用反向的深度优先搜索降回第0层,
 *     从而找到一条增广路
 *   – 每找到一条增广路,就将其经过的顶点及其关联的边从图中
 *     临时删除(标记),以确保与后续找到的增广路无公共顶点
 *   算法的终止条件:
 *   找不到增广路 ⇒ 无增广路 ⇒ 找到最大匹配
****************************************************************************/
#include <bits/stdc++.h>
#include <algorithm>
#define endl "\n"
#define inf 0x7fffffff
const int MAXN = 3050;
using namespace std;
int t,n,m;
struct node{

    int x, y, speed;
};
struct edge     // 邻接表建图
{
    int u, v, next;
};
int head[MAXN], tot;
edge e[MAXN*MAXN];
node umbrella[MAXN], guest[MAXN];
// lx[i]为左侧顶点匹配的右侧顶点标号
// rx[i]为右侧顶点匹配的左侧顶点标号
// dx[i]为左集合i的距离顶点标号
// dy[i]为右集合i的距离顶点标号
int lx[MAXN], rx[MAXN], dx[MAXN], dy[MAXN];
int vis[MAXN];  // 寻找增广路的标记数组
bool Valid(node a, node b)
{
    int dis, tt;
    dis = (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
    tt = a.speed*a.speed*t*t;
    if(tt >= dis)return true;
    return false;
}
void add(int u, int v)  // 加边,此题中规定时间内可达即可匹配的条件并以此建立二分图
{
    e[tot].u = u;
    e[tot].v = v;
    e[tot].next = head[u];
    head[u] = tot++;
}
int bfs()   //  找增广轨集合
{
    int dis = inf;
    memset(dx, -1, sizeof(dx));
    memset(dy, -1, sizeof(dy));
    queue<int> Q;
    for(int i = 1; i <= n; i++)
    {
        if(lx[i] == -1)
        {
            Q.push(i);  // 左集合未匹配顶点i入队
            dx[i] = 0;  // 左侧零层顶点距离为0
        }
    }
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        if(dx[u] > dis) break;
        for(int i = head[u]; i != -1; i = e[i].next)
        {
            int v = e[i].v;
            if(dy[v] == -1) // 如果右侧集合点i还未走过
            {
                dy[v] = dx[u]+1;    // 右侧v的距离(层次)标号为左侧u的距离标号+1
                if(rx[v] == -1) dis = dy[v]; // 如果未匹配,则直接匹配
                else        // 有了就反回回去
                {
                    dx[rx[v]] = dy[v]+1; // 匹配的那个点
                    Q.push(rx[v]);
                }
            }
        }
    }
    return dis != inf;      // dis = inf 则说明没有增广路集合, 否则返回长度为dis的增广路径集合
}

int dfs(int u)      // 找相应的增广路
{
    for(int i = head[u]; i != -1; i = e[i].next)
    {
        int v = e[i].v;
        if(!vis[v] && dx[u]+1 == dy[v]) // 右侧点v的距离编号是左侧点u的距离+1
        {
            vis[v] = 1;
            if(rx[v] == -1 || dfs(rx[v])) // 右侧点v没有与之匹配的左侧点或者能找到一条增光轨
            {
                rx[v] = u;
                lx[u] = v;      // uv匹配并返回一条增广路
                return 1;
            }
        }
    }
    return 0;
}
int MaxMatch()                  // 求最大匹配
{
    int ret = 0;
    memset(lx, -1, sizeof(lx));
    memset(rx, -1, sizeof(rx));
    while(bfs())                // 能找到增广路径集合
    {
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= n; i++)
            if(lx[i] == -1)     // 左侧顶点i未匹配,看是否能找到增广轨
                ret += dfs(i);
    }
    return ret;
}
void Init()
{
    memset(head, -1, sizeof(head));
    tot = 0;    // 总边数
    cin >> t >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> guest[i].x >> guest[i].y >> guest[i].speed;
    }
    cin >> m;
    for(int i = 1; i <= m; i++)
    {
        cin >> umbrella[i].x >> umbrella[i].y;
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            if(Valid(guest[i], umbrella[j]))    // 规定时间内人能够跑到雨伞处则建边
            {
                add(i,j);
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    int T, ret;
    cin >> T;
    for(int cas = 1; cas <= T; cas++)
    {
        Init();
        cout << "Scenario #" << cas << ":\n" << MaxMatch() << endl << endl;
    }
    return 0;
}


Java版本:
import java.io.BufferedInputStream;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

/**
 * Created by Phoebus on 2017/3/31.
 * E-mail: huangnanhan@outlook.com
 */
public class Main {

    static final int MAXN = 3050;
    static final int inf = 0x7fffffff;
    static int t, n, m;

    static public class node {
        int x, y, speed;
    }
    static public class edge {
        int u, v, next;
    }
    static int head[] = new int[MAXN];
    static int tot;

    static node umbrella[] = new node[MAXN];
    static node guest[] = new node[MAXN];
    static edge e[] = new edge[MAXN * MAXN];

    static int lx[] = new int[MAXN];
    static int rx[] = new int[MAXN];
    static int dx[] = new int[MAXN];
    static int dy[] = new int[MAXN];
    static boolean vis[] = new boolean[MAXN];

    public static boolean isValid(node a, node b) {
        int dis, tt;
        dis = (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
        tt = a.speed * a.speed * t * t;
        if(tt >= dis) return true;
        return  false;
    }

    public static void add(int u, int v) {
        e[tot] = new edge();
        e[tot].u = u;
        e[tot].v = v;
        e[tot].next = head[u];
        head[u] = tot++;
    }

    public static boolean bfs() {
        int dis = inf;
        for(int i = 0; i < MAXN; ++i) {
            dx[i] = dy[i] = -1;
        }
        Queue<Integer> Q = new LinkedList<Integer>();
        for(int i = 1; i <= n; ++i) {
            if(lx[i] == -1) {
                Q.offer(i);
                dx[i] = 0;
            }
        }
        while(!Q.isEmpty()) {
            int u = Q.poll();
            if(dx[u] > dis) break;
            for(int i = head[u]; i != -1; i = e[i].next) {
                int v = e[i].v;
                if(dy[v] == -1) {
                    dy[v] = dx[u] + 1;
                    if(rx[v] == -1) dis = dy[v];
                    else {
                        dx[rx[v]] = dy[v] + 1;
                        Q.offer(rx[v]);
                    }
                }
            }
        }
        return dis != inf;
    }

    public static boolean dfs(int u) {
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(!vis[v] && dx[u]+1 == dy[v]) {
                vis[v] = true;
                if(rx[v] == -1 || dfs(rx[v]) == true) {
                    rx[v] = u;
                    lx[u] = v;
                    return true;
                }
            }
        }
        return false;
    }

    public static int MaxMatch() {
        int ret = 0;
        for(int i = 0; i < MAXN; ++i) {
            lx[i] = rx[i] = -1;
        }
        while(bfs() == true) {
            for(int i = 0; i < MAXN; ++i) vis[i] = false;
            for(int i = 1; i <= n; ++i) {
                if(lx[i] == -1) {
                    if(dfs(i) == true) ret++;
                }
            }
        }
        return ret;
    }

    static Scanner cin = new Scanner(new BufferedInputStream(System.in));
    static int cas = 1, T;

    public static void Init() {
        try {
            for (int i = 0; i < MAXN; ++i) head[i] = -1;
            tot = 0;
            t = cin.nextInt();
            n = cin.nextInt();

            for (int i = 1; i <= n; ++i) {
                guest[i] = new node();
                guest[i].x = cin.nextInt();
                guest[i].y = cin.nextInt();
                guest[i].speed = cin.nextInt();
            }
            m = cin.nextInt();
            for (int i = 1; i <= m; ++i) {
                umbrella[i] = new node();
                umbrella[i].x = cin.nextInt();
                umbrella[i].y = cin.nextInt();
            }
            for (int i = 1; i <= n; ++i) {
                for (int j = 1; j <= m; ++j) {
                    if (isValid(guest[i], umbrella[j]) == true) {
                        add(i, j);
                    }
                }
            }
            System.out.print("Scenario #");
            System.out.print(cas);
            System.out.println(":");
            System.out.println(MaxMatch());
            System.out.println("");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        T = cin.nextInt();
        for(cas = 1; cas <= T; cas++) {
            Init();
        }
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值