zoukankan      html  css  js  c++  java
  • UVALIVE 4970 最小权匹配

    首先贴一下这道题的BNU地址,UVA地址自己找吧。

    http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=11852

    题意:这道题的意思就是,给你N个棋子的坐标,这些棋子的走法是象棋中的马的走法。然后再给你N个坐标终点。

    问所有的棋子走到其中一个坐标上,不能有重复,最少的步数是多少。

    思路:这道题直接搜显然爆,因为他的坐标范围都是int ,所以我们就要考虑怎么处理出两个点之间的最短步数。

    我直接把问题扔给队友了,然后他推出了一条公式,太神不能多说。

    问题简化成给你两个点(x , y ) ,(x1 , y1),求这两个点之间最少需要多少步。

    然后他经过一系列的数学推导,就搞定了。

    过程我也不会,等下看代码。

    然后接下来每个棋子到每个终点坐标的距离都处理出来了。

    那么根据N = 15 。那么很显然状压DP就可以搞定,求出最小值。

    但是考虑到每个棋子只有一个位置,那么很显然棋子和终点位置是二分图,那么很显然我们可以用最小权匹配来搞这个问题。

    KM的姿势比状压优美许多。

    #include <set>
    #include <map>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <string>
    #include <vector>
    #include <iomanip>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define Max 2505
    #define FI first
    #define SE second
    #define ll long long
    #define PI acos(-1.0)
    #define inf 1ll << 60ll
    #define LL(x) ( x << 1 )
    #define bug puts("here")
    #define PII pair<int,int>
    #define RR(x) ( x << 1 | 1 )
    #define mp(a,b) make_pair(a,b)
    #define mem(a,b) memset(a,b,sizeof(a))
    #define REP(i,s,t) for( int i = ( s ) ; i <= ( t ) ; ++ i )
    
    using namespace std;
    
    ll f(ll x,ll y) {//推出来的,太神不多说
        x=abs(x);
        y=abs(y);
        if(x>y) {
            swap(x,y);
        }
        if(x==0&&y==1) {
            return 3;
        }
        if(x==2&&y==2) {
            return 4;
        }
        ll z=max((y+1)/2ll,(x+y+2)/3ll);
        if((z-x-y)&1) {
            z++;
        }
        return z;
    }
    int n ;
    ll x[22] , y[22] ;
    ll xx[22] , yy[22] ;
    ll Map[22][22] ;
    ll lx[22] ,ly[22] ,linkx[22] , linky[22] ;
    bool visx[22] , visy[22] ;
    int find(int now){
        visx[now] = 1 ;
        for (int i = 0 ; i < n ; i ++ ){
            if(!visy[i] && Map[now][i] - lx[now] - ly[i] == 0){
                visy[i] = 1 ;
                if(linky[i] == -1 || find(linky[i])){
                    linkx[now] = i ;
                    linky[i] = now ;
                    return 1 ;
                }
            }
        }
        return 0 ;
    }
    ll KM(){
        mem(linkx , -1) ;
        mem(linky , -1) ;
        mem(ly ,0) ;
        for (int i = 0 ; i < n ; i ++ ){
            while(1){
                mem(visx ,0) ;mem(visy , 0) ;
                if(find(i))break ;
                ll d = inf ;
                for (int j = 0 ; j < n ; j ++ ){
                    if(visx[j])
                        for (int k = 0 ; k < n ; k ++ ){
                            if(!visy[k])
                                d = min(d , Map[j][k] - lx[j] - ly[k]) ;
                        }
                }
                for (int j = 0 ; j < n ; j ++ ){
                    if(visx[j])lx[j] += d ;
                    if(visy[j])ly[j] -= d ;
                }
            }
        }
        ll ans = 0 ;
        for (int i = 0 ; i < n ; i ++ ){
            if(linky[i] != -1 )ans += Map[linky[i]][i] ;
        }
        return ans ;
    }
    int main() {
        int ca = 0 ;
        while(cin >> n , n ){
            for (int i = 0 ; i < n ; i ++ )cin >> x[i] >> y[i] ;
            for (int i = 0 ; i < n ; i ++ )cin >> xx[i] >> yy[i] ;
            for (int i = 0 ; i < n ; i ++ )lx[i] = inf ;
            for (int i = 0 ; i < n ; i ++ ){
                for (int j = 0 ; j < n ; j ++ ){
                    Map[i][j] = f(x[i] - xx[j] , y[i] - yy[j]) ;
                    lx[i] = min(lx[i] , Map[i][j]) ;
                }
            }
            cout << ++ ca << ". " ;
            cout << KM() << endl;
        }
        return 0 ;
    }


  • 相关阅读:
    POJ2528——Mayor's posters (线段树区间更新查询+离散化)
    C++STL——unique函数总结
    HDU 5618 Jam's problem again(CDQ分治+树状数组(三维模板题))
    c++解决爆栈,手动加栈!
    POJ1741——Tree (树分治之点分治)
    树分治之点分治模板总结
    CodeForces
    字典树
    卡特兰数高精度算法
    基数排序
  • 原文地址:https://www.cnblogs.com/riskyer/p/3364692.html
Copyright © 2011-2022 走看看