Q - Tour[KM算法+拆点]

Q - Tour

题意大概是:一个王国有N个城市,M条路,都是有向路。现在要求去旅游,不过走的路只能是环(至少也需要有两个城市),即从某个城市开始,从某个城市结束。他们保证这些城市之间的路径都是有环构成的,求至少需要走多少路。

脑洞:乍一看,以为是最短路或者是欧拉路之类的问题。但后来才发现,二分图的一个性质就是每个匹配点的入度和出度都为1(这肯定是匹配之后的)。我们把每一个点拆成两部分,一部分是入读一部分是出度,我们把出度的那些点放入X集合中,把入度的那些点放入Y集合中。
而题意已经说明了所有的点都会被走到,那么说明这道题一定是一个最佳匹配。
然后这里使用的是 O ( n 3 ) O(n^3) O(n3)的写法,多了一个slack数组。
然后这道题我们刚开始要把所有的权值都变为负值,最后在转换回来。这样做的原因是,我们的KM算法求出的答案是权值和的最大值。而这道题要求的是最小值。那么我们先把权值都变为负值,求出他权值和的最大值,当我们把他反过来的时候,那便是我们要求的答案,也就是权值和的最小值。
其余的一些解释请看代码的注释。

int w[len][len];
int vis_x[len], vis_y[len], wx[len], wy[len];//x,y的标记数组以及权值数组
int l[len], slack[len];
int N, M, T;
int a, b, c;
void Init(){
//没得到一组数据,我们刚开始要把数据都读进图和x的数组。所以这两个数组我们赋成负的无穷大。
    memset(w, -INF, sizeof(w));
    memset(wx, -INF, sizeof(wx));
    memset(wy, 0, sizeof(wy));
    memset(vis_x, 0, sizeof(vis_x));
    memset(vis_y, 0, sizeof(vis_y));
    memset(l, 0, sizeof(l));
}
int main(){
    cin >> T;
    while (T--){
        Init();
        cin >> N >> M;
        for (int i=0; i<M; i++){
            cin >> a >> b >> c;
            w[a][b] = max(w[a][b], -c);
            wx[a] = max(wx[a], w[a][b]);
        }

        int ans = KM();

        cout << ans << endl;
    }
    return 0;
}
int KM(){
    for (int i=1; i<=N; i++){
        memset(slack, INF, sizeof(slack));//slack数组表示的是每一个x y需要松弛的度数。

        while(true){
            memset(vis_x, 0, sizeof(vis_x));
            memset(vis_y, 0, sizeof(vis_y));

            if (find(i)) break;
            //先找点,匹配上的话,我们让下一个点去匹配。
			//如果匹配不上,我们开始对这个点进行松弛处理。
			//slack数组里面存的数据,是让x的权值降到使最近的那个点符合相加等于边长,而这条边不是最长边
            int d = INF;

            for (int j=1; j<=N; j++){
                if (!vis_y[j] && d > slack[j])
                    d = slack[j];
            }//找最小的slack

            for (int j=1; j<=N; j++){
                if (vis_x[j]) wx[j] = wx[j] - d;
                if (vis_y[j]) wy[j] = wy[j] + d;
            }

        }
    }

    int sum = 0;

    for (int i=1; i<=N; i++)
        sum = sum + wx[i] + wy[i];

    return -sum;
}
bool find (int p){
    vis_x[p] ++;
    for (int i=1; i<=N; i++){
        if (!vis_y[i] && wx[p]+wy[i] == w[p][i]){
            vis_y[i] ++;
            if (!l[i] || find(l[i])){
                l[i] = p;
                return true;
            }
        }else if (!vis_y[i]) slack[i] = min(slack[i], wx[p]+wy[i]-w[p][i]);
    }

    return false;
}

KM算法详解+模板
最近又找到一个厉害的博主的厉害的博客。

期末大作业基于python的足球运动员数据分析源码+数据集(高分项目),个人经导师指导并认可通过的高分设计项目,评审分98分,项目中的源码都是经过本地编译过可运行的,都经过严格调试,确保可以运行!主要针对计算机相关专业的正在做大作业、毕业设计的学生和需要项目实战练习的学习者,资源项目的难度比较适中,内容都是经过助教老师审定过的能够满足学习、使用需求,如果有需要的话可以放心下载使用。 期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于python的足球运动员数据分析源码+数据集(高分项目)期末大作业基于pyth
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值