HDU ~ 4370 ~ 0 or 1 (建图(难想) + Dijkstra)

本文探讨了一个基于矩阵的图论问题,旨在通过构造特定矩阵来寻找从节点1到节点N的最短路径,同时考虑了特殊的环路情况。采用Dijkstra算法实现,并通过巧妙的标志位处理来寻找最小环的费用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给你一个矩阵C,让你构造一个只有01的矩阵X使 ∑C[i][j]*X [i][j](1<=i,j<=n) 最小,输出这个最小值。

构造出来得X矩阵需要满足一下关系:

1.X 12+X 13+...X 1n=1 

2.X 1n+X 2n+...X n-1n=1 

3.for each i (1<i<n), satisfies ∑X ki (1<=k<=n)=∑X ij (1<=j<=n)。

思路:超级难想的转化,其实对于这三个条件我们可以把理解为一下三个条件:

1.1点出度为1

2.n点的入度为1

3.其他点的出度入度相等。

那么问题就转化了,其实就是让我们求一条到1~N的最短路。但是会漏掉一种情况,1的最小环花费+n的最小环花费,且不能是自环(1,1和n,n点没有在1,2条件中),因为只规定了1点的出度为1,n点的入度为1。没规定1点的入度和n点的出度,所以min(1~N的最短路,1的最小环花费+n的最小环花费)就是答案。

这样就可以直接建图了,因为给的矩阵就相当于邻接矩阵。最短路好求,但是怎么求最小环花费呢。

我采取的方法是在求最短路的时候,立flag来进行计算,初始化flag=0,表示此时还没有1的环,即我之前没有碰到过一条u(!=s)~v(=s)的的边(即该边起点U不为S但终点V等于S)。如果碰到了,此时我们把d[s]更新且使flag=1表示已经有1的环了,以后再碰到这样的边两个环花费取较小值即可。

在我代码中也就是这两句话:

if (u != s && e.to == s && flag) d[s] = min(d[s], d[e.from] + e.dist);//之前碰见过了,两种环取一个环花费较小的
if (u != s && e.to == s && !flag) { flag = true; d[s] = d[e.from] + e.dist; }//第一次碰见终点为s的边且不是自环,那么直接赋值。


#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
const int INF = 0x3f3f3f3f;

struct Edge
{
    int from, to, dist;
    Edge(int u, int v, int w): from(u), to(v), dist(w) {}
};

struct Dijkstra
{
    int n, m;
    vector<Edge> edges;
    vector<int> G[MAXN];
    int d[MAXN];
    bool vis[MAXN];
    int pre[MAXN];

    void init(int n)
    {
        this->n = n;
        edges.clear();
        for (int i = 0; i <= n; i++) G[i].clear();
    }

    void add_edge(int from, int to, int dist)
    {
        edges.push_back(Edge(from, to, dist));
        m = edges.size();
        G[from].push_back(m - 1);
    }

    struct HeapNode
    {
        int from, dist;
        bool operator < (const HeapNode& rhs) const
        {
            return rhs.dist < dist;
        }
        HeapNode(int u, int w): from(u), dist(w) {}
    };

    void dijkstra(int s)
    {
        for (int i = 0; i <= n; i++) d[i] = INF;
        memset(vis, 0, sizeof(vis));
        bool flag = false;//更新最小环用
        d[s] = 0;
        priority_queue<HeapNode> Q;
        Q.push(HeapNode(s, d[s]));
        while (!Q.empty())
        {
            HeapNode x = Q.top(); Q.pop();
            int u = x.from;
            if (vis[u]) continue;
            vis[u] = true;
            for (int i = 0; i < G[u].size(); i++)
            {
                Edge& e = edges[G[u][i]];
                if (u != s && e.to == s && flag) d[s] = min(d[s], d[e.from] + e.dist);
                if (u != s && e.to == s && !flag) { flag = true; d[s] = d[e.from] + e.dist; }
                if (!vis[e.to] && d[e.to] > d[u] + e.dist)
                {
                    d[e.to] = d[u] + e.dist;
                    pre[e.to] = G[u][i];
                    Q.push(HeapNode(e.to, d[e.to]));
                }
            }
        }
    }
};
Dijkstra solve;
int main()
{
    int n;
    while (~scanf("%d", &n))
    {
        solve.init(n);
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                int t; scanf("%d", &t);
                solve.add_edge(i, j, t);
            }
        }
        solve.dijkstra(1);
        int ans = solve.d[n], loop1 = solve.d[1];
        solve.dijkstra(n);
        int loopn = solve.d[n];
        ans = min(ans, loop1 + loopn);//1~n的最短路和(1的最小环花费 + n的最小环花费)
        printf("%d\n", ans);
    }
    return 0;
}
/*
4
1 2 4 10
2 0 1 1
2 2 0 5
6 3 1 2
*/




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值