Live2D

Solution -「Gym 102759C」Economic One-way Roads

Description

  Link.

  给定一个含 n 个点 m 条边的简单无向图,每条边的两种定向方法各有权值,求使得图强连通且定向权值和最小的方法。

  n18

Solution

  涉及到叫做“耳分解”的知识点。

有向图 G=(V,E) 是否强连通有以下判别方法:

  • 取任意 uV,令点集 S={u}
  • 反复取 x,yS,以及连接 x,y 的一条有向路径 P=x,u1,,uk,y,满足 uiS, i[1,k],并令 SS{u1,,uk}
  • S=V,则 G 强连通;否则即找不到增广路 PG 非强连通。

其中 P 就是一个“耳”,这就是“耳分解”。

——当然“耳”貌似最初定义于无向图。

  知道了这个构造强连通图的 trick 就极简了,首先在双向边权中随便选一个预支付代价,并令 f(S) 表示在 S 的导出子图内使 S 强连通的最小代价,g(S,x,y,0/1) 表示点集 S 中,当前正在构造的“耳”从 x 出发,希望回到 y,不能/能 直接走 x,y 这条边。随便转移即可。复杂度上限是 O(2nn3),但算法本身和状态合法性带来了小常数√

Code

/*~Rainybunny~*/

#include <cstdio>
#include <cstring>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

const int MAXN = 18, IINF = 0x3f3f3f3f;
int n, adj[MAXN + 5][MAXN + 5], f[1 << MAXN], g[1 << MAXN][MAXN][MAXN][2];

inline void chkmin( int& a, const int b ) { b < a && ( a = b ); }
inline int imin( const int a, const int b ) { return a < b ? a : b; }

int main() {
    // freopen( "data.in", "r", stdin );

    scanf( "%d", &n );
    rep ( i, 0, n - 1 ) rep ( j, 0, n - 1 ) scanf( "%d", &adj[i][j] );

    int ans = 0;
    rep ( i, 0, n - 1 ) rep ( j, i + 1, n - 1 ) {
        if ( ~adj[i][j] ) {
            int t = imin( adj[i][j], adj[j][i] );
            ans += t, adj[i][j] -= t, adj[j][i] -= t;
        }
    }

    memset( f, 0x3f, sizeof f ), memset( g, 0x3f, sizeof g ), f[1] = 0;
    rep ( S, 1, ( 1 << n ) - 1 ) if ( S & 1 ) {
        rep ( u, 0, n - 1 ) if ( S >> u & 1 ) {
            rep ( v, 0, n - 1 ) if ( S >> v & 1 && ~adj[u][v] ) {
                chkmin( f[S], g[S][u][v][1] + adj[u][v] );
            }
        }

        rep ( u, 0, n - 1 ) if ( S >> u & 1 ) {
            rep ( v, 0, n - 1 ) if ( S >> v & 1 ) {
                chkmin( g[S][u][v][0], f[S] );
            }
        }

        rep ( u, 0, n - 1 ) if ( S >> u & 1 ) {
            rep ( v, 0, n - 1 ) if ( S >> v & 1 ) {
                int* cur = g[S][u][v];
                if ( cur[0] != IINF ) {
                    rep ( w, 0, n - 1 ) if ( ~adj[u][w] && !( S >> w & 1 ) ) {
                        chkmin( g[S | 1 << w][w][v][u != v],
                          cur[0] + adj[u][w] );
                    }
                }
                if ( cur[1] != IINF ) {
                    rep ( w, 0, n - 1 ) if ( ~adj[u][w] && !( S >> w & 1 ) ) {
                        chkmin( g[S | 1 << w][w][v][1], cur[1] + adj[u][w] );
                    }
                }
            }
        }
    }

    printf( "%d\n", f[( 1 << n ) - 1] == IINF ? -1 : ans + f[( 1 << n ) - 1] );
    return 0;
}

posted @   Rainybunny  阅读(245)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示