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;
}

 

posted @ 2013-02-19 18:45  Titanium  阅读(550)  评论(0编辑  收藏  举报