最值比较中将INF设置为0x3f3f3f3f为何好?

本文探讨了在算法竞赛中,如何选择合适的'无穷大'值,0x3f3f3f3f因其避免溢出和适用于多种场景的优势,相较于0x7fffffff成为更好的选择。作者解释了两者之间的区别,并揭示了0x3f3f3f3f的巧妙之处,尤其是在处理大整数运算和数组初始化时的实用性。

在算法竞赛中,我们常常需要用到一个“无穷大”的值,对于我来说,大多数时间我会根据具体问题取一个99999999之类的数(显得很不专业啊!)

在网上看别人代码的时候,经常会看到他们把INF设为0x7fffffff,奇怪为什么设一个这么奇怪的十六进制数,一查才知道,因为这是32-bit int的最大值。如果这个无穷大只用于一般的比较(比如求最小值时min变量的初值),那么0x7fffffff确实是一个完美的选择。

但是更多情况下,0x7fffffff并不是一个好的选择,比如在最短路径算法中,我们使用松弛操作:

if (d[u]+w[u][v]<d[v]) d[v]=d[u]+w[u][v];

如果u,v之间没有边,那么w[u][v]=INF,如果我们的INF取0x7fffffff,那么d[u]+w[u][v]会溢出而变成负数,我们的松弛操作便出错了!

准确来说,0x7fffffff不能满足“无穷大加一个有穷的数依然是无穷大”这个条件,它会变成了一个很小的负数。

更进一步的,如果有一个数能够满足“无穷大加无穷大依然是无穷大”,那么就更好了!

前阵子无意中看到了一个不一样的取值,INF=0x3f3f3f3f,这时我又郁闷了,这个值又代表的是什么?于是我去寻找答案,发现这个值的设置真的很精妙!

0x3f3f3f3f的十进制是1061109567,是10^9级别的(和0x7fffffff一个数量级),而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。
另一方面,由于一般的数据都不会大于10^9,所以当我们把无穷大加上一个数据时,它并不会溢出(这就满足了“无穷大加一个有穷的数依然是无穷大”),事实上0x3f3f3f3f+0x3f3f3f3f=2122219134,这非常大但却没有超过32-bit int的表示范围,所以0x3f3f3f3f还满足了我们“无穷大加无穷大还是无穷大”的需求。

最后,0x3f3f3f3f还能给我们带来一个意想不到的额外好处:
如果我们想要将某个数组清零,我们通常会使用memset(a,0,sizeof(a)),方便又高效,但是当我们想将某个数组全部赋值为无穷大时,就不能使用memset函数而得自己写循环了,因为memset是按字节操作的,它能够对数组清零是因为0的每个字节都是0(一般我们只有赋值为-1和0的时候才使用它)。现在好了,如果我们将无穷大设为0x3f3f3f3f,那么奇迹就发生了,0x3f3f3f3f的每个字节都是0x3f!所以要把一段内存全部置为无穷大,我们只需要memset(a,0x3f,sizeof(a))。

所以在通常的场合下,0x3f3f3f3f真的是一个非常棒的选择!

检查代码:#include<bits/stdc++.h> #define LL int using namespace std; const int N=205,INF=0x3fffff; int n,s,t,mp[N][N]; LL du[N],cur[N]; LL dfs(int u,LL full){ if(u==t){ return full; } for(int &x=cur[u];x<=t;x++){ if(mp[u][x] && du[x]==du[u]+1){ LL df=dfs(x,min(full,mp[u][x])); if(df){ mp[u][x]-=df; mp[x][u]+=df; return df; } } } return 0; } bool bfs(int s,int t){ memset(du,0,sizeof(du)); queue<int>q; q.push(s); du[s]=1; while(!q.empty()){ int u=q.front(); q.pop(); for(int x=1;x<=t;x++){ if(mp[u][x] && !du[x]){ du[x]=du[u]+1; q.push(x); } } } return du[t]; } LL dinic(int s,int t){ LL res=0,cnt; while(bfs(s,t)){ memset(cur,0,sizeof(cur)); while(cnt=dfs(s,0x3f3f3f3f)){ res+=cnt; } } return res; } int get(int x,int y,int f){ return (x-1)*n+y+(f?n*n:0); } int main(){ cin>>n; s=0; t=2*n*n+1; int x,y,val; while(cin>>x>>y>>val,x||y||val){ mp[get(x,y,0)][get(x,y,1)]=val; } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(i<n){ mp[get(i,j,1)][get(i+1,j,0)]=INF; } if(j<n){ mp[get(i,j,1)][get(i,j+1,0)]=INF; } } } mp[s][get(1,1,0)]=2; mp[get(n,n,1)][t]=2; cout<<dinic(s,t); return 0; } 设有N*N的方格图(N<=10,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。 某人从图的左上角的A 点出发,可以向下行走,也可以向右走,直到到达右下角的B点。在走过的路上,他可以取走方格中的数(取走后的方格中将变为数字0)。 此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为大。 输入格式: 输入的第一行为一个整数N(表示N*N的方格图),接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。 输出格式: 只需输出一个整数,表示2条路径上取得的大的和。 提示: http://www.cnblogs.com/TheRoadToTheGold/p/6506196.html 样例 1 : 输入: 8 2 3 13 2 6 6 3 5 7 4 4 14 5 2 21 5 6 4 6 3 15 7 2 14 0 0 0 输出: 67
09-03
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HHppGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值