poj1639 Picnic Planning 最小度数限制生成树「建议收藏」

poj1639 Picnic Planning 最小度数限制生成树

大家好,又见面了,我是全栈君。

题意:若干个人开车要去park聚会,可是park能停的车是有限的,为k。所以这些人要通过先开车到其它人家中,停车,然后拼车去聚会。另外,车的容量是无限的,他们家停车位也是无限的。

求开车总行程最短。

       就是求一最小生成树,可是对于当中一个点其度不能超过k。

思路:

1. 将park点取出 将剩下的点求出最小生成树  出现i个联通块

2. 再每一个块中选择与park点相邻的最小边  

到此park点已经连了i条边

park点最大能够连接k个点

得到Sum值

3. 须要求出i+1–>k 条的Sum值

每次加入一条边在树上形成一个环 然后 删去一条环上的边(权值最大)取Sum=min(Sum,Sum+加入边-删去边)  复杂度O(n^2)

由于第三步复杂度高须要优化第三步

优化:先记录Vi->Vp路径上且不与Vp直接相连的边的权值的Max[ i ]

加入边时 取cost(Vi,Vp)-Max [ i ]最小值 加入(Vi,Vp)边

再枚举ViVp原有的路径上不与Vp相连的边,找到最大权值的边;

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <malloc.h>
#include <ctype.h>
#include <math.h>
#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
const int maxn =111+5;
const int maxe = 15000+5;
const int INF = 460002326;
#include <map>
map<string,int>mp;
map<string,int>::iterator it;
int car,n,cost[maxn][maxn],sum,father[maxn];
int best[maxn];
bool vis[maxn];
bool edge[maxn][maxn];
bool use[maxn];
void dfs(int root)//将一个连通块中各个点标记其father
{
    for(int i=1; i<n; i++)
    {
        if(vis[i]&&edge[root][i])
        {
            father[i]=root;
            vis[i]=false;
            dfs(i);
        }
    }
}
void prim(int s)
{
    int Min_i,Min,dis[maxn],num[maxn];
    memset(vis,false,sizeof(vis));
    for(int i=0; i<n; i++)
    {
        dis[i]=cost[i][s];
        num[i]=s;//此时dis[i]的值来自哪个点
    }
    dis[s]=0;
    vis[s]=use[s]=true;
    while(true)
    {
        Min=INF,Min_i=-1;
        for(int i=0; i<n; i++)
        {
            if(!use[i]&&!vis[i]&&(Min_i==-1||Min>dis[i]))
            {
                Min_i=i;
                Min=dis[i];
            }
        }
        if(Min==INF)    break;
        sum+=Min;
        vis[Min_i]=true;
        use[Min_i]=true;//标记连通块用过的点
        edge[Min_i][num[Min_i]]=edge[num[Min_i]][Min_i]=true;
        for(int i=0; i<n; i++)
        {
            if(!use[i]&&!vis[i]&&dis[i]>cost[i][Min_i])
            {
                num[i]=Min_i;
                dis[i]=cost[i][Min_i];
            }
        }
    }
    Min=INF;
    int root=-1;
    for(int i=0; i<n; i++)//寻找该连通块到Park点的最小距离
    {
        if(vis[i]&&cost[0][i]<Min&&i!=0)//在这棵树中
        {
            Min=cost[0][i];
            root=i;
        }
    }
    vis[root]=false;
    dfs(root);//以root为根
    father[root]=0;
    sum+=Min;
}
int Best(int j)//更新当中各个点到park路径上边权值最大的点
{
    if(father[j]==0)//假设father为0,记为-1
        return best[j]=-1;
    if(best[j]!=-1) return best[j];//假设已经存在 。直接返回
    int tmp=Best(father[j]);
    if(tmp!=-1)//这说明其父节点不与park相连
    {
        if(cost[tmp][father[tmp]]>cost[father[j]][j])
            best[j]=tmp;
        else best[j]=j;
    }
    else best[j]=j;//其父节点与source相连  将j赋给自己
    return best[j];
}
void solve()
{
    int mst=0;
    memset(father,-1,sizeof(father));
    memset(use,0,sizeof(use));
    memset(edge,false,sizeof(edge));
    use[0]=true;
    for(int i=0; i<n; i++)
    {
        if(!use[i])//use用过要标记
        {
            prim(i);//除Park外建最小生成树
            mst++;
        }
    }
    for(int i=mst+1; i<n&&i<=car; i++)
    {
        memset(best,-1,sizeof(best));
        for(int j=0; j<n; j++)
        {
            if(j!=0&&best[j]==-1&&father[j]!=0)
                    Best(j);
        }
        int minadd=INF;
        int ax,bx,change;
        for(int j=0; j<n; j++)
        {
            if(cost[0][j]!=INF&&father[j]!=0)
            {
                ax=best[j];
                bx=father[ax];
                if(minadd>cost[0][j]-cost[ax][bx])//cost[0][j]表示加入的边 cost[ax][bx]表示断开的边
                {                                                       
                    minadd=cost[0][j]-cost[ax][bx];//更新减小的值以及连接的点
                    change=j;
                }
            }
        }
        if(minadd>=0)   //表示要添加sum值    则已经得到最小的sum值
            break;
        sum+=minadd;//更新
        ax=best[change];
        bx=father[ax];
        cost[ax][bx]=cost[bx][ax]=INF;
        father[change]=0;
        cost[0][change]=cost[change][0]=INF;
    }
}
int main()
{
    int t;
   // freopen("in.txt","r",stdin);
    cin>>t;
    mp.clear();
    string s1,s2;
    int val;
    for(int i=0; i<maxn; i++)
        for(int j=0; j<maxn; j++)
            cost[i][j]=INF;
    n=1,sum=0;
    mp["Park"]=0;//Park为0;
    for(int i=0; i<t; i++)
    {
        cin>>s1>>s2>>val;
        it=mp.find(s1);//map映射值
        if(it==mp.end())
            mp[s1]=n++;
        it=mp.find(s2);
        if(it==mp.end())
            mp[s2]=n++;
        if(cost[mp[s1]][mp[s2]]>val)//可能会有重边。其实没有。。。。

。 cost[mp[s1]][mp[s2]]=cost[mp[s2]][mp[s1]]=val; } cin>>car; solve(); cout<<"Total miles driven: "<<sum<<endl; return 0;}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/116345.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 多线程中 ManualResetEvent 的用法[通俗易懂]

    多线程中 ManualResetEvent 的用法[通俗易懂]///<summary>///手动重启///</summary>privateManualResetEventmanualReset=newManualR

    2022年7月1日
    26
  • pcl点云匹配_点云教程

    pcl点云匹配_点云教程逐步匹配多幅点云

    2022年10月22日
    0
  • 利用cmd命令进入mysql数据库

    利用cmd命令进入mysql数据库1.打开cmd。2.输入电脑上mysql安装的盘路径:之后回车3.输入完整的mysql.exe安装路径:cdD:\mysql\bin 之后回车4.输入mysal-hlocalhost-uroot(数据库名称)-p*****(数据库的密码)之后回车注:每个-前都有空格5.此时已进入mysql数据库,可以根据showdatabases;语句显示现有的数据库6.也可以对数据库进行操作,例如

    2022年5月31日
    32
  • ROC曲线的通俗理解

    ROC曲线的通俗理解在准备机器学习导论课程考试的过程中,发现自己根据西瓜书上的讲解总是也理解不上去ROC曲线的含义。于是在网络上寻求答案,发现一篇讲解得不错的博客【1】,说得比西瓜书好很多,通俗易懂。这里说一下自己的感想和理解对于已经有数据标签的训练样本,可以得到它们的评分: 其中class一栏表示真实值,p为正例,n为反例,这20个样本中有10个正例10个反例;score一栏则是分类器给出的分类评分。一…

    2022年5月16日
    48
  • mysql一主多从 读写分离_mysql读写分离原理

    mysql一主多从 读写分离_mysql读写分离原理简介:什么是主从复制,如何实现读写分离,看这篇你就懂了!思维导图文章已收录到我的Github精选,欢迎Star:https://github.com/yehongzhi/learningSummary前言在很多项目,特别是互联网项目,在使用MySQL时都会采用主从复制、读写分离的架构。为什么要采用主从复制读写分离的架构?如何实现?有什么缺点?让我们带着这些问题开始这段学习之旅吧!为什么使用主从复制、读写分离主从复制、读写分离一般是一起使用的。目的很简单,就是为了提高数据库的并发性能。你想,假设是单机,读

    2022年8月13日
    5
  • IDEA注释快捷键[通俗易懂]

    IDEA注释快捷键[通俗易懂]IDEA可以使用快捷键添加行注释Ctrl+/、块注释Ctrl+Shift+/,还可以快速生成类注释、方法注释等,下面就介绍这几种快捷键的用法.[1]行注释Ctrl+/首先你的光标要处于这一行,处于这行的哪个位置都可以,按Ctrl+/,就可以往行首添加”//”,将该行注释掉。再次按Ctrl+/,可以去掉该行注释。[2]块注释Ctrl+Shift+/使用块注释需要先选中要注释的块。…

    2022年8月15日
    26

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号