zoukankan      html  css  js  c++  java
  • poj 1062 昂贵的聘礼

    最短路 OR 搜索

    这题大多数是归为图论题,求最短路径,看了一下题目,确实是这样的意思,但是我却很难想到怎么最短路。后来觉得搜索更形象易懂所以写了一个dfs,但是wa,然后看了一下解题报告才发现有个地方理解错题意了。根据题意做了小修改就过了

    题意:其实就是找一条最短路径,但是路径中任意两个点的等价差不能超过限制值(我一开始的理解是两个直接相连的点的等级差不能超过限制值)

    /*
    建图:一个物品就是一个点,一个物品能容另一种物品来换,那么就是两个物品间可能相连,
    为什么是可能,因为还要两个物品的等级满足条件。因为物品数上限为100,用邻接矩阵来
    建图方便。
    再注意一点,这是个有向图,要得到i物品可以用j物品来换并加上优惠价,那么有向边为j-->i,
    wji=优惠价
    终点固定为1,其实就是求从哪个点出发去到1时的路径和最小,注意一点,路径的起点,也就是那个
    物品,是一定要花钱买的不能换的(不要漏掉这个),然后就是加上路径上的边权就是我们的答案
    */
    /*
    用搜索来做。设d[i]为得到i物品的最小花费,那么我们要求的就是d[1]。d[1]初始化为它本身的价格
    更好(也就是没有进行任何交换直接用钱买的价格),这样做可以处理掉图不连通的情况或者一些特殊
    的数据。而d[1]=min{ w(1,k)+d[k] } 其中这些k物品和1号物品相连并且k这个点不会破坏等级限制,

    也就是说加入k这个点,整个路径任意两个点的等级差不会超过限制值。
    那么我们就递归地去算出d[k]。
    有点类似记忆化搜索,但是初始化d[i]为它本身的价格(即任意一个物品不需要交换直接购买)
    */

    下面的代码跑出了0ms,不过再提交多几次的话因RP问题会偶然出现16ms

    #include <cstdio>
    #include <cstring>
    #define N 110
    #define INF 0x3f3f3f3f
    
    struct node
    {
        int val,l,n;
    }a[N];
    int g[N][N],d[N],vis[N];
    int n,M;
    
    void dfs(int v ,int maxl , int minl)
    {
        if(vis[v]) return ;
    
        vis[v]=1;
        int max,min;
        for(int u=1; u<=n; u++) if(g[u][v]!=INF) //有边
        {
            max=maxl>a[u].l?maxl:a[u].l;
            min=minl<a[u].l?minl:a[u].l;
            if(max-min<=M)  //在路径中任意点的等级差不能超过M
            {
                dfs(u,max,min);
                if(g[u][v]+d[u]<d[v])
                    d[v]=g[u][v]+d[u];
            }
        }
    }
    
    void solve()
    {
        memset(vis,0,sizeof(vis));
        dfs(1,a[1].l,a[1].l);
        printf("%d\n",d[1]);
    }
    
    void build()
    {
        memset(g,0x3f,sizeof(g));
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d%d",&a[i].val,&a[i].l,&a[i].n);
            //该物品本身的价值,等级,有多少个替代品
            d[i]=a[i].val;
            for(int j=1; j<=a[i].n; j++)
            {
                int m,w;
                scanf("%d%d",&m,&w);
                g[m][i]=w;  //先保存但不一定是有边
            }
        }
    }
    
    int main()
    {
        while(scanf("%d%d",&M,&n)!=EOF)
        {
            build();
            solve();
        }
        return 0;
    }

    用最短路来求解。用最短路来求解,要解决的一个最困难的问题就是等价限制,题目要求整个路径中任意两个点的等价差不能超过M,那么如果运行最短路算法的话显然不能满足这个要求。后来查看解题报告才发现了正确的做法,就是先枚举出一个等价范围,然后把等级在这个范围内的点找出来,再这些点中就能进行最简单的最短路算法

    那么枚举等级区间是什么意思呢?我们知道,1号物品是我们最终要买下的东西,它必然存在,而他本身有一个等级L,那么我们可以知道,其余物品中的最小等级只能是L-M,最大等级只能是L+M。也就是说我们可以以1号物品的等级L为中心然后进行枚举一个区间,这个区间的长度一定是M,L一定在区间内;那么第一个就是[L-M,L],第二个就是[L-M+1,L+1],第三个就是[L-M+2,L+2]……最后一个就是[L,L+M],可以看出这个区间枚举是很巧妙的

    每得到一个区间就把等级在区间的点标记,用这些标记的点来运行DIJ,以1号顶点为源点求到其他顶点的最短路(其实也就是其余各点到1号顶点的最短路),这还没完,还要加上这些物品本身的价值(因为相当于是从这些顶点回到1号顶点,这些顶点是起点,起点物品时一定要花钱买的),然后找出最小的d[i]值,也就是说从这个顶点回到1号花费最少。

    /*
    1.邻接矩阵建图
    2.枚举等级区间
    3.每次枚举后以1号顶点为源点运行一遍dij
    4.每次运行dij都将得到一个最小值,取最小的那个就是最后答案
    注意这里的建图同样是有向边,不过方向反过来,i物品可以用j物品加上钱来换
    那么有向边i-->j
    */

    这个代码同样跑出了0MS

    /*
    1.邻接矩阵建图
    2.枚举等级区间
    3.每次枚举后以1号顶点为源点运行一遍dij
    4.每次运行dij都将得到一个最小值,取最小的那个就是最后答案
    注意这里的建图同样是有向边,不过方向反过来,i物品可以用j物品加上钱来换
    那么有向边i-->j
    */
    
    #include <cstdio>
    #include <cstring>
    #define N 110
    #define INF 0x3f3f3f3f
    
    struct node
    { int val,l,n; }a[N];
    int g[N][N];
    int n,M;
    bool within[N];
    
    int DIJ(int s)
    {
        int d[N];
        bool used[N];
    
        memset(d,0x3f,sizeof(d));
        memset(used,0,sizeof(used));
        d[s]=0;
    
        for(int nn=1; nn<n; nn++)
        {
            int min=INF,x=s;
            for(int i=1; i<=n; i++) if(!used[i] && d[i]<min)
            { min=d[i]; x=i;}
            used[x]=1;
            for(int i=1; i<=n; i++)
                if(within[i] && d[x]+g[x][i] < d[i])
                    d[i]=d[x]+g[x][i];
            //这个松弛要做出些微的改变,必须是本次枚举中的点才能用
            //就是靠within[i]来做判断
        }
    
        int mincost=INF;
        for(int i=1; i<=n; i++)
        {
            d[i]+=a[i].val;
            mincost=d[i]<mincost?d[i]:mincost;
        }
        return mincost;
    }
    
    void solve()
    {
        //以1号顶点的等级为中心来枚举等级区间
        int x,y,ans;  //闭区间[x,y]
        ans=INF;
        for(x=a[1].l-M; x<=a[1].l; x++)
        {
            y=x+M;
            memset(within,0,sizeof(within));
            for(int i=1; i<=n; i++)
                if(a[i].l>=x && a[i].l<=y)
                    within[i]=1;
            int m=DIJ(1);
            ans=m<ans?m:ans;
        }
        printf("%d\n",ans);
    }
    
    void build()
    {
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                g[i][j]=i==j?0:INF;
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d%d",&a[i].val,&a[i].l,&a[i].n);
            for(int j=1; j<=a[i].n; j++)
            {
                int v,w;
                scanf("%d%d",&v,&w);
                g[i][v]=w;
            }
        }
    }
    
    int main()
    {
        while(scanf("%d%d",&M,&n)!=EOF)
        {
            build();
            solve();
        }
        return 0;
    }
  • 相关阅读:
    insert 和 if x is not None
    python3和Python2的区别(被坑太久了)
    python面试题大全
    Python里的拷贝=====》很容易错误的
    Python中函数参数传递问题
    重回:类,对象,方法,属性
    30 个 Python 语言的特点技巧
    centos7下使用yum安装mysql
    phpinfo.php
    添加开机启动项命令
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2917551.html
Copyright © 2011-2022 走看看