一.重载比较操作符函数
优先队列中类型为结构体类型的排序方法,需要重载比较操作符函数
重载“<”操作符的函数可以写在结构体里面,也可以写在结构体外面,写在结构体外面的时候,记得函数的参数要使用引用。
第一种重载方法(重载“<”操作符的函数写在结构体外面,函数的参数要使用引用):
struct node
{
int x,y;
int step;
};
priority_queue<node>q; //优先队列中元素的比较规则默认是按元素的值从大到小排序;
bool operator<(const node &a,const node &b) //括号里面是const 而且还必须是引用
{
return a.step>b.step; //从小到大排序。重载小于号。因为默认是从大到小
}
第二种重载方法(重载“<”操作符的函数写在结构体里面,函数的参数不需要使用引用):
struct node
{
int x,y;
int time; //定义一个优先队列
friend bool operator<(node a, node b)
{ //从小到大排序采用“>”号;如果要从大到小排序,则采用“<”号
return a.time> b.time; //从小到大排序
}
};
二.优先队列+bfs结合的题型
例题:
坦克要从起点(Y),到目的地(T),坦克不能通过钢墙(S),河(R),可以在空地在行走(E),射击破坏砖墙(B),射击砖墙时不行走且花费一个单位的时间,在空地上行走时也花费一个单位的时间。求坦克从起点到目的地最少花多少时间,不可达输出-1;
思路:
此题用到了广搜的思想,只是在出队时做了处理,利用优先队列让队列中到起点的时间值最小的点先出队。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
struct node {
int x,y,s;
friend bool operator <(node a,node b)
{
return a.s>b.s;
}
};
char map[305][305];
int n,m;
int vis[305][305];
int dir[][2]={
{0,1},{0,-1},{1,0},{-1,0}
};
int sx,sy,ex,ey;
bool ok(int x,int y)
{
if(x>=0&&x<n&&y>=0&&y<m&&(map[x][y]!='S'&&map[x][y]!='R'))
return true;
return false ;
}
int bfs()
{
priority_queue<node> q;
node head;
memset(vis,1,sizeof(vis));
head.x=sx,head.y=sy,head.s=0;
q.push(head);
vis[sx][sy]=0;
while(!q.empty())
{
node f=q.top();
q.pop();
if(f.x==ex&&f.y==ey)
return f.s;
for(int i=0;i<4;i++)
{
int dx=dir[i][0]+f.x,dy=f.y+dir[i][1];
if(ok(dx,dy)&&vis[dx][dy])
{
vis[dx][dy]=0;
int temp;
if(map[dx][dy]=='B') temp=2;
else temp=1;
node ss;
ss.x=dx,ss.y=dy,ss.s=f.s+temp;
q.push(ss);
}
}
}
return -1;
}
int main()
{
while(scanf("%d%d",&n,&m)&&n+m)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>map[i][j];
if(map[i][j]=='Y') sx=i,sy=j;
else if(map[i][j]=='T') ex=i,ey=j;
}
}
printf("%d\n",bfs());
}
return 0;
}
总结:以上是bfs+ 优先队列 题的模板,把思想搞懂了,其实都是一个套路。
三. 二分法及其应用
二分法是快速查找已排序的数组中某一元素的经典算法。其时间复杂度只有O(log n)。但二分法的应用不仅限于此,很多在一个单调区间内的判断问题以及两个量成一定的正比或反比的关系的问题都可以用二分法解决,但这种类型题对二分的应用比较隐蔽,需要我们熟悉。
例题:
将n根网线切成k段相同长度的网线,问可切成的最长长度是多少;
思路:因为每次当切的长度长时,则段数少;当切的长度短时,则长度长。所以切的长度和段数成一个反比的关系。利用二分发查找答案,每次偏右查找(因为要查找大的)
代码:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
using namespace std;
#define exp 1e-8
int n,k;
double a[10005],sum;
int judge(double s)
{
int cnt = 0;
for(int i = 0; i<n; i++)
{
cnt+=(int)(a[i]/s);
}
if(cnt>=k) return 1;
return 0;
}
int main()
{
int i;
while(~scanf("%d%d",&n,&k),n+k)
{
sum = 0;
for(i = 0; i<n; i++)
{
scanf("%lf",&a[i]);
sum+=a[i];
}
sum = sum/k;
double l = 0,r = sum;
while(fabs(l-r)>exp)
{
double mid = (l+r)/2;
if(judge(mid))
l = mid;
else
r = mid;
}
printf("%.2f\n",l);
}
return 0;
}
四.对做一个系统代码的理解
1. 以前我总是不明白为什么老师不让我们做看似很完整精妙的界面以及写上各种提示语句。今天被费老师一讲,好像明白了一些。包括我在内的很多学生只注重于界面的制作,但却忽略了真正重要的:系统功能的实现。光在那里做界面啦,而其实实现的功能却寥寥无几。可谓是做足了表面功夫。
2.对于写提示语句的问题,这个费老师不让我们写也是为了让我们对自己写出来的系统有很好的掌握。即使没有提示语句,依然能够知道自己应该怎样输入,这样能够有利于促进自己理清思路再写代码,而不是糊里糊涂的写完代码一步步跟着提示语句往下运行。
3.费老师还说了最重要的一点,那就是每写完一个功能函数都要在下面写一个主函数调用此功能函数运行一下。以防出现我以前出现的问题:哗哗哗把整个系统的代码敲完了,功能函数一个也不能正常使用,在重头再去调代码。现在做的都是一些最基本的系统,各个功能函数之间没有什么联系,从头调起来还比较简单。可到了后面,要做击败莱航的大系统时,写完一个功能函数,就马上用主函数调用一下看能否实现你想达到的功能,确保每个功能函数都正确。