码迷,mamicode.com
首页 > 其他好文 > 详细

HDU6094 Rikka with K-Match

时间:2020-02-13 12:48:35      阅读:76      评论:0      收藏:0      [点我收藏+]

标签:min   直线   fill   最大   ++   with   time   print   math   

Rikka with K-Match

Yuta has a graph \(G\) with \(n\) nodes \((i,j)(1 \leq i \leq n,1 \leq j \leq m)\). There is an edge between \((a,b)\) and \((c,d)\) if and only if \(|a-c|+|b-d|=1\). Each edge has its weight.

Now Yuta wants to calculate the minimum weight \(K\)-matching of \(G\).

\(1 \leq n \leq 4 \times 10^4,1 \leq m \leq 4\)

题解

https://blog.csdn.net/The_star_is_at/article/details/76919415

应该是一个经典的 Idea 了。令 \(w_i\) 为匹配数为 \(i\) 时的最小匹配,那么因为匹配是一个费用流问题,所以一定有 \(w_{i+1}-w_{i} \geq w_{i}-w_{i-1}\)

考虑把 \(w_i\) 看成平面上的点 \((i,w_i)\),那么显然所有点都分布在一个下凸壳上,因此肯定存在一个斜率 \(d\),使得这个斜率的直线与下凸壳的切点恰好为 \((K,w_K)\),即 \((K,w_K)\)\(w_i-id\) 最小的点,这相当于把边权减去 \(d\) 后的最小匹配。

因此可以二分斜率 \(d\),然后求出边权全部减去 \(d\) 后的最小匹配的值以及最小匹配中有多少条边,根据最小匹配中的边数和 \(K\) 的大小关系来决定二分的方向。这样问题就转化成了求 \(O(\log n)\) 次网格图最大匹配。这是一个轮廓线 DP 的经典问题,可以在 \(O(nm2^m)\) 内解决。

因此总的时间复杂度为 \(O(nm2^m\log n)\)

要注意的是二分上界不能设为 \(10^9\) 级别,考虑一条 \(1\)\(10^9\) 交错的链,那么在最后一次增广的时候所有的 \(1\) 都会变成 \(10^9\),因此二分上界应该是 \(10^9 \times \frac{nm}{2}\),标程把上界设为了 \(10^{14}\),DP 时刚好不会爆 long long

CO int64 inf=1e18;
int n,m,A[40001][4],B[40001][4];
int64 f[2][5][1<<4];int g[2][5][1<<4];

pair<int64,int> solve(int64 mid){
    for(int i=0;i<=m;++i) fill(f[0][i],f[0][i]+(1<<m),inf);
    f[0][m][(1<<m)-1]=g[0][m][(1<<m)-1]=0;
    int o=1;
    for(int u=1;u<=n;++u){
        for(int i=0;i<=m;++i) fill(f[o][i],f[o][i]+(1<<m),inf);
        copy(f[o^1][m],f[o^1][m]+(1<<m),f[o][0]);
        copy(g[o^1][m],g[o^1][m]+(1<<m),g[o][0]);
        for(int i=0;i<m;++i)for(int s=0;s<1<<m;++s){
            if(i<m-1){
                int t=s|1<<i|1<<(i+1);
                int64 v=f[o][i][s]+B[u][i]-mid;
                int c=g[o][i][s]+1;
                if(f[o][i+2][t]>v or (f[o][i+2][t]==v and g[o][i+2][t]<c))
                    f[o][i+2][t]=v,g[o][i+2][t]=c;
            }
            if(u>1 and ~s>>i&1){
                int t=s|1<<i;
                int64 v=f[o][i][s]+A[u-1][i]-mid;
                int c=g[o][i][s]+1;
                if(f[o][i+1][t]>v or (f[o][i+1][t]==v and g[o][i+1][t]<c))
                    f[o][i+1][t]=v,g[o][i+1][t]=c;
            }
            int t=s&~(1<<i);
            int64 v=f[o][i][s];
            int c=g[o][i][s];
            if(f[o][i+1][t]>v or (f[o][i+1][t]==v and g[o][i+1][t]<c))
                f[o][i+1][t]=v,g[o][i+1][t]=c;
        }
        o^=1;
    }
    int64 ans=inf;int K=0;
    for(int s=0;s<1<<m;++s)
        if(ans>f[o^1][m][s] or (ans==f[o^1][m][s] and K<g[o^1][m][s]))
            ans=f[o^1][m][s],K=g[o^1][m][s];
    return make_pair(ans,K);
}
void real_main(){
    read(n),read(m);
    int K=read<int>();
    for(int i=1;i<n;++i)for(int j=0;j<m;++j) read(A[i][j]);
    for(int i=1;i<=n;++i)for(int j=0;j<m-1;++j) read(B[i][j]);
    int64 l=0,r=1e14;
    while(l<r){
        int64 mid=(l+r)>>1;
        if(solve(mid).second>=K) r=mid;
        else l=mid+1;
    }
    printf("%lld\n",solve(l).first+l*K);
}
int main(){
    for(int T=read<int>();T--;) real_main();
    return 0;
}

HDU6094 Rikka with K-Match

标签:min   直线   fill   最大   ++   with   time   print   math   

原文地址:https://www.cnblogs.com/autoint/p/12302969.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!