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