zoukankan      html  css  js  c++  java
  • 野餐计划

    题目
    大佬讲解
    这个是算法进阶指南上面的题目,ACwing上面的秦大佬讲的很详细

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1010;
    #define quick() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)//读入优化
    #define mem(a,b) memset(a,b,sizeof(a))//初始化
    #define init() mem(g,0),mem(vis,false),root=cnt=tot2=tot=0,q.clear(),q2.clear()//初始化大军
    #define add(a,b,c) g[a][b]=g[b][a]=c//无向图加边
    #define mk(a,b) make_pair(a,b)//简便
    int tot2,tot,n,m,fa[N],cnt,c,s,root,g[31][31],dis[31][31];
    //tot2为边的数量
    //tot为点的数量
    //cnt为编号的数量
    map<string,int> q;//映射关系
    map<pair<int,int>,bool> q2;//统计这条边是否出现在最小生成树中
    int vis[N];//标记连通块的编号
    string a,b;
    int find(int x)
    {
        return x==fa[x]?fa[x]:fa[x]=find(fa[x]);//并查集找红太阳
    }
    struct edge1
    {
        int x,y,w;
    } g2[N];
    int cmp (edge1 a,edge1 b)
    {
        return a.w<b.w;//最小边排序
    }
    struct node
    {
        int u,v,d;//(u,v)节点权值为d
        inline void inits()
        {
            u=v=0;
            d=-1;
        }
    } dp[N];
    struct edge2
    {
        inline void add_edge(int a,int b,int c)//加入一条边
        {
            g2[++tot2].x=a;//起点
            g2[tot2].y=b;//终点
            g2[tot2].w=c;//权值
        }
        inline int kruskal()
        {
            sort(g2+1,g2+1+tot2,cmp);//排序,找最小
            int ans=0;//我们的目标连通块的连通块编号
            for(int i=1; i<=tot2; i++)
            {
                int x=find(g2[i].x),y=find(g2[i].y);//求出所在连通块
                if (x==1 || y==1 || x==y)//不是目标连通块,或者已经在一起了
                    continue;
                fa[x]=y;//合并
                ans+=g2[i].w;//统计
                q2[mk(g2[i].x,g2[i].y)]=true;//这条边出现过
                q2[mk(g2[i].y,g2[i].x)]=true;
            }
            return ans;
        }
    } g3;
    void read()
    {
        quick();
        init();
        cin>>n;
        root=q["Park"]=tot=1;//Park节点就是我们的一号节点
        for(int i=1; i<=n; i++)
        {
            cin>>a>>b>>c;
            if (!q[a])//名字读入
                q[a]=(++tot);//新编号
            if (!q[b])
                q[b]=(++tot);//新编号
            g3.add_edge(q[a],q[b],c);//加边
            add(q[a],q[b],c);//加边
            fa[i]=i;//初始化每个点的父亲节点
        }
        cin>>s;
    }
    void dfs(int x)
    {
        for(int j=2; j<=tot; j++)
            if (g[x][j] && !vis[j])//有边,但是木有被标记
            {
                vis[j]=cnt;
                dfs(j);
            }
    }
    void pd()//连通块划分
    {
        for(int i=2; i<=tot; i++)
            if (!vis[i])
            {
                cnt++;//又来一个
                vis[i]=cnt;
                dfs(i);
            }
    }
    void dfs(int now,int last)//计算(1,x)路径上最大边
    {
        for(int i=2; i<=tot; i++)
        {
            if(i==last || !q2[mk(now,i)])//点重叠,或者没有这条边
                continue;
            if(dp[i].d==-1)//没有来过
            {
                if(dp[now].d>g[now][i])
                    dp[i]=dp[now];
                else
                {
                    dp[i].u=now;
                    dp[i].v=i;
                    dp[i].d=g[now][i];
                }
            }
            dfs(i,now);
        }
    }
    void work()
    {
        pd();
        int ans=g3.kruskal();//统计每一个连通块的值
        for(int i=1; i<=cnt; i++)
        {
            int now=0x3f3f3f3f,st=0;//初始值为INF
            for(int j=2; j<=tot; j++)
                if (vis[j]==i)//属于这个连通块
                    if (now>g[1][j] && g[1][j]!=0)
                    {
                        now=g[1][j];//找到与1相连最小的边
                        st=j;
                    }
            ans+=now;//将每一个连通块与1相连
            q2[mk(1,st)]=q2[mk(st,1)]=true;
        }
        int t=cnt;
        while(s>t)
        {
            s--;
            int now=0,idx=0;
            for(int i=1; i<=1100; i++)
                dp[i].inits();
            dfs(1,-1);//求每一个点到1的途中最大边是谁?
           // 这个dp我看了好久才发现可能表示到i点路径最大权值
            for(int j=2; j<=tot; j++)
            {
                if(now<dp[j].d-g[1][j] && g[1][j])
                {
                    now=dp[j].d-g[1][j];//找到最大权值边
                    idx=j;
                }
            }
            if (now<=0)//已经不会多优秀了
                break;
            ans=ans-now;
            q2[mk(dp[idx].u,dp[idx].v)]=false;//删除边
            q2[mk(1,idx)]=q2[mk(idx,1)]=true;//添加边
        }
        cout<<"Total miles driven: "<<ans;
    }
    int main()
    {
        read();
        work();
        return 0;
    }   
    
    作者:秦淮岸灯火阑珊
    链接:https://www.acwing.com/solution/acwing/content/2488/
    来源:AcWing
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

    秦大佬真是太强了

    每次做题提醒自己:题目到底有没有读懂,有没有分析彻底、算法够不够贪心、暴力够不够优雅。
  • 相关阅读:
    ztree实现树的异步加载
    form submit提交遇到的问题
    js页面刷新常用的几种方式
    IE6下的按钮效果
    实现两个div并排的三种方式
    继续推荐Android12个自测源码
    优秀程序员实现向卓越程序员跨越
    面试那点事【面试题+面试技巧+职位推荐】
    Android 开发源码分享
    Android 亲测源码分享
  • 原文地址:https://www.cnblogs.com/spnooyseed/p/12870906.html
Copyright © 2011-2022 走看看