您的位置:首页 > 其它

UVA 10537 The Toll! Revisited(最短路变形+输出字典序最小路径)

2014-03-03 14:29 447 查看
题目:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1478

题目大意:要运送东西,路过一个地方要给过路费,有两种,一是村庄,给 1 个物资,二是城镇,给 当前物资数/20 的物资,不足20当20算。给你 n 条边,每条边给你两个端点,小写的表示村庄,大写的表示城镇。现在给你起点和终点,还有你要运往终点的物资,问你在起点最少需要准备多少的物资,输出来,然后打印字典序最小的路径。

解题思路:从终点逆着进行更新,用 d[ i ] 表示已经进入了 i 再从 i 到达终点需要准备的最小物资数。那么 d[ e ] 很明显就是输入的那个数。然后就是更新了,dij 和 spfa 都可以,就把所有的 d 都算出来了,然后从起点根据 d 递归打印路劲就行了。两个地方需要注意:(1)由于是d 是逆着算的,有一个地方,我纠结了很久,就是城镇那里,你知道了进入城镇后剩余的量,那怎么求进入之前的量,想了很久,公式退步出来,后来看了人家博客才发现,原来很简单,不用推公式,想想如果 x/20 不是向上取整,
x - x/20 = y ,那么 x = 20*y/19,那么现在是向上取整,那么这个 x 肯定是 >= 20*y/19 。然后就是 ++ 暴力往上算凑即可,最多次数是多少,我不知道怎么算(回去后有时间再算算。。 = =),我试验了一下,这个次数很少的,对复杂度没有影响。(2)输出路径的时候,根据 d 进行输出,是递归满足 d[ u ] - w = d[ v ] 的最小的 v ,所以只要从小到大枚举 v 即可,这个w 就很好算了,这是正序的算。

  还有一点需要总结,个人原因,此题错了 n 次,最后发现有两个地方:(1)由于这道题数据范围比较大,要用 long long ,我有的中间变量就是用的 int ,还是多注意吧,以后大不了把 int 全改成 long long 算了。(2)路径打印的地方,应该是 a-b ,我竟然是 a->b ,不想多说什么了,脑残啊。。

代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

typedef long long lld;

const int MAXN = 333;
const lld INF = 0x0fffffffffffffff;

int Map[333][333];

struct Node
{
int id;
lld val;
Node(int iid,lld vval)
{
id = iid;
val = vval;
}
bool operator < (const Node& tmp) const
{
if(val == tmp.val)
{
return id < tmp.id;
}
else return val > tmp.val;
}
};

priority_queue<Node> q;

lld d[MAXN];

int done[MAXN];

int is_town(int x)
{
if(x <= 'Z' && x >= 'A') return 1;
else return 0;
}

void dij(int load,char s,char e)
{
for(int i = 0;i < 256;i++)
d[i] = INF;
d[s] = load;
q.push(Node(s,load));
memset(done,0,sizeof(done));
while(!q.empty())
{
Node cur = q.top();
q.pop();
if(done[cur.id]) continue;
done[cur.id] = 1;
for(int i = 0;i < 256;i++)
{
if(Map[cur.id][i] == 0) continue;
int next_id = i;
lld tmp;
if(is_town(cur.id))
{
for(tmp = cur.val*20/19;;tmp++)
{
if(tmp-(tmp+19)/20 >= cur.val)
break;
}
}
else tmp = cur.val+1;
if(tmp < d[next_id])
{
d[next_id] = tmp;
q.push(Node(next_id,tmp));
}
}
}
}

void print(int u,int e)
{
if(u == e)
{
printf("%c\n",e);
return ;
}
printf("%c-",u);
for(int i = 0;i < 256;i++)
if(Map[u][i] == 1)
{
lld tmp;
if(is_town(i)) tmp = d[u]-(d[u]+19)/20;
else tmp = d[u]-1;
if(d[i] == tmp)
{
print(i,e);
break;
}
}
}

int main()
{
int cas = 0;
int m;
while(~scanf("%d",&m))
{
if(m == -1) break;
memset(Map,0,sizeof(Map));
char str[5],str1[5];
while(m--)
{
scanf("%s%s",str,str1);
Map[str[0]][str1[0]] = 1;
Map[str1[0]][str[0]] = 1;
}
int load;
scanf("%d%s%s",&load,str,str1);
dij(load,str1[0],str[0]);
printf("Case %d:\n",++cas);
printf("%lld\n",d[str[0]]);
print(str[0],str1[0]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: