做题小感悟(随有随记)

时间复杂度

在竞赛中,一般计算一秒能运行5 * 10 ^ 8次计算, 一般 算法数据范围:
O(n) : n < 10 ^ 8;
O(n logn) : n <= 10 ^ 6;
O(n sqrt(n)) n < 10 ^ 5;
O(n ^ 2): n < 5000;
O(n ^ 3): n < 300
O(2 ^ n): n < 25;
O(n!): n < 11

搜索00

dfs
精髓:递归。

  1. 要用到递归:参数的传入和return退出本次递归
  2. dfs中循环的变量i的含义(表示填入的行数?填入的数字?)及其中现场的还原
  3. 对角线 dg[i + x] , 反对角线udg[u - x + i]的标记。两者开的大小为 2 * n。
  4. 记录路径的数组,根据所求答案的性质而来。

bfs
精髓:队列。

  1. 方向数组 dx[] = {}, dy[] = {};
    2.难免用到队列,结构体(存储的坐标、步数等);
    typedef pair <int, int> PII; PII s[N * N] , int x = s[i].first + dx[i], y = s[i].second + dy[i];
  2. 有的时候,我们不必开一个book数组判断该点走没走过。直接一个d[x][y]初始化全部为-1, 当走过时,更新为走的步数。
  3. 很重要的一个问题: 判断是否出界。
  4. 关于八数码问题:
    字符串存储状态,map存储的为字符串及对应的步数。
    寻找目标值x, 调用字符串中find函数,int k = t.find(‘x’)如何转化为坐标? int i = k / 3, j = k % 3;
    主义的一个细节: 循环为4次(上下左右)每次都需要还原回去状态,使得对上下左右是公平的。

搜索

与素数相结合

题目大意:给出两个四位数的素数,求a变换到b需要几步,要求变幻时只有一个数字不同,并且是素数。

【直接自敲】小编上来直接bfs,错因(1)prime是质数的意思,大意了啊,直接bfs;(2)脑子是个好东西。。我直接两重循环 记录偏移量的数组dn[] = {1, 10, 100, 1000}, j为 [0,9], 每次原数增加dn[i] * j, 违背了只改变一个数如 1230 + 80 = 1310, 改变了两个数了。。
【正确思路】

  1. 仔细审题,不合法时,输出impossible,审题不细
  2. 打印一个素数表,判断是否为素数直接count(n)即可。
  3. 针对第二个问题:我们开一个数组,记录这四个数字。

细节
4. 第高位不能为0, 每次必须只改变一个数, 注意写bfs时continue的写法
5. 两者相等时,直接输出 “0”, 需要特判。
6. t[j] = tmp, 四位数,当我们讨论完任意一个,继续讨论下一个时,我们要把之前讨论的还原回去。
7. 判断该数是不是为质数,小编用的筛法并把所有质数加到一个map中, mp.insert({i, 1}), mp.count(i)即可。但我们也可开一个数组st, st[i] = true or false, 直接判断该数是不是质数

代码

大家闲的时候可以看看,解解闷。复习一下bfs, emmm

#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <map>
using namespace std;
const int N = 10010;
int n, m;
int book[N];
int t[4];
int primes[N];
struct f{
    int x;
    int s;
};
queue <f> qu;
map <int, int> mp;


int bfs(void){
    memset(book, 0, sizeof(book));
    while(qu.size()) qu.pop();
    int tmp;
    struct f a;
    a.x = n;
    a.s = 0;
    qu.push(a);
    book[n] = 1;
    while(!qu.empty()){
        struct f b = qu.front();
        qu.pop();
        t[0] = b.x / 1000;
        t[1] = b.x % 1000 / 100;
        t[2] = b.x % 100 / 10;
        t[3] = b.x % 10;
        for(int j = 0; j < 4; j ++){
            tmp = t[j];
            for(int i = 0; i < 10; i ++){
                if(i == tmp) continue;
                if(j == 0 && i == 0) continue;
                t[j] = i;
                struct f c;
                c.x = t[0] * 1000 + t[1] * 100 + t[2] * 10 + t[3];
                c.s = b.s + 1;
                if(book[c.x] == 0 && mp.count(c.x)){
                    book[c.x] = 1;
                    qu.push(c);
                    if(c.x == m){
                        return c.s;
                    }
                }
            }
            t[j] = tmp;
        }
    }
    return -1;

}

int main(void){
    int t;
    cin >> t;
    int cnt = 0;
    for(int i = 2; i <= 10000; i ++){
        if(!book[i]){
            mp.insert({i, 1});
            primes[cnt ++] = i;
        }
        for(int j = 0; primes[j] <= 10000 / i; j ++){
            book[primes[j] * i] = 1;
            if(i % primes[j] == 0) break;
        }
    }
  //  printf("%d\n", cnt);
    while(t --){
        cin >> n >> m;
        if(n == m){
            cout << "0" << endl;
            continue;
        }
       int ans = bfs();
       if(ans == -1) cout << "Impossible" << endl;
       else cout << ans <<endl;
    }
    return 0;
}

双起点搜索

题目描述:
两人在n*m的平地上同时放火,’#‘表示草,’.’ 表示石头, 两人分别选一个’#'格子点火,火可以向上向下向左向右在有草的格子蔓延,点火的地方时间为0,蔓延至下一格的时间依次加一。求烧完所有的草需要的最少时间。如不能烧完输出-1。
思路

  1. 找出所有草地的坐标, 存储到一个结构体数组中,两两组合枚举。
  2. 双起点bfs跟普通的差不大,只是在加入起点时,加入两个。其他没有不同
  3. 判断每组情况烧没烧完,在bfs’返回后,设置一个book数组:if(g[i][j] == '#' && book[i][j] == 0) 则这种组合烧法不合题意。
  4. 如何判断有无解,设置一个flag, 有符合题意的解,就设置为1,最后判断flag为1, 还是0即可。

Fire and run out ?

题目大意:
一个人要从火场里出来,火的起点“F”可能有多个,但人“J”只有一个,火可以上下左右蔓延,人不能走墙“#”,不能走火;问人是否能出来,最少多久
题目分析
多起点bfs, 人和火需要一起讨论(bfs压进队列,完美解决该问题),但又要区分人和火(可用两个数组标记人火走没走过的情况, 如下代码 b[2][100000], 第一层下标为0表示火走过的, 第二层下标为1表示人走过的, 同时, 在用来表示位置、时间的结构体里也应该设置一个no,0表示火,1表示人, 分别讨论 )
代码亮点:

  1. bfs函数中,表示设置一个flag变量,当循环完毕后, 判断flag是否为0, 便知有无解
  2. 输入时,直接找出了所有火的位置,以便同时传入bfs中队列里·
  3. 最新鲜的是, 设置一个变量 no表示 1表示人, 0 表示火, 这要的讨论,写法,很棒!!
  4. 注意:先把火压进队列,在压进去人。
    【放下代码。 有兴趣的小伙伴们可以看看哦】
#include <iostream>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int r, c;
char mp[N][N];
struct node{
    int x, y, time, no;
}fire[1000009];

void bfs(int jx, int jy, int I){
    bool b[2][1010][1010];
    memset(b, 0, sizeof(b));
    queue <struct node> que;
    struct node t, tt;
    for(int i = 1; i <= I; i ++){
        t = fire[i];
        b[0][t.x][t.y] = true;
        que.push(t);
    }
    t.y = jy, t.x = jx, t.time = 0, t.no = 1;
    que.push(t);
    b[1][t.x][t.y] = true;
    int d[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
    bool flag = false;
    while(!que.empty()){
        t = que.front();
        if(t.no == 1 && (t.x == r || t.x == 1 || t.y == c || t.y == 1)){
            flag = true;
            break;
        }
        que.pop();
        for(int i = 0; i <= 3; i ++){
            tt.x = t.x + d[i][0];
            tt.y = t.y + d[i][1];
            tt.time = t.time + 1;
            tt.no = t.no;
            if(tt.x < 1 || tt.x > r || tt.y < 1 || tt.y > c) continue;
            if(tt.no == 1){
                if(b[1][tt.x][tt.y] == 0 && b[0][tt.x][tt.y] == 0 && mp[tt.x][tt.y] == '.'){
                    que.push(tt);
                    b[tt.no][tt.x][tt.y] = true;
                }
            }
            if(tt.no == 0){
                if(b[0][tt.x][tt.y] == 0 && mp[tt.x][tt.y] == '.'){
                    que.push(tt);
                    b[tt.no][tt.x][tt.y] = true;
                }
            }
        }
    }
    if(flag){
        printf("%d\n", t.time + 1);
    }
    else printf("IMPOSSIBLE\n");
}

int main(void){
    int t, jx, jy;
    scanf("%d", &t);
    while(t --){
        scanf("%d%d", &r, &c);
        getchar();
        int I = 0;
        for(int i = 1; i <= r; i ++){
            for(int j = 1; j <= c; j ++){
                scanf("%c", &mp[i][j]);
                if(mp[i][j] == 'J'){
                    jx = i, jy = j;
                }
                else if(mp[i][j] == 'F'){
                    fire[++ I].y = j;
                    fire[I].x = i;
                    fire[I].time = 0;
                    fire[I].no = 0;
                }
            }
            getchar();
        }
        bfs(jx, jy, I);
    }
    return 0;
}

dfs解决连通问题

问题描述:
就是找出有多少块有石油的区域,石油对应数组中的@,这边相邻指的是是周围的八个位置。

细节
dfs函数中,如果该点是石油(@), 我们对他周围的八个点都要进行dfs处理(因为连通块不一定是规则的图形)
【放下代码,主要是复习dfs】

#include <algorithm>
#include <iostream>
using namespace std;
const int N = 110;
int d[8][2] = {-1, 0, -1, 1, 0, 1, 1, 1, 1, 0, 1, -1, 0, -1, -1, -1};
int m, n, flag;
char str[N][N];

void dfs(int x, int y){
    if(str[x][y] == '*') return ;
    flag = 1;
    str[x][y] = '*';
    int dx, dy;
    for(int i = 0; i < 8; i ++){
        dx = x + d[i][0];
        dy = y + d[i][1];
        if(dx < 0 || dx >= m || dy < 0 || dy >= n) continue;
        dfs(dx, dy);
    }
    return ;

}

int main(void){
    while(1){
        scanf("%d%d", &m, &n);
        int num = 0;
        if(m == 0) return 0;
        for(int i = 0; i < m; i ++) scanf("%s", str[i]);
        for(int i = 0; i < m; i ++){
            for(int j = 0; j < n; j ++){
                flag = 0;
                dfs(i, j );
                if(flag){
                    num ++;
                }
            }
        }
        printf("%d\n", num);
    }
}

双起点(2)

题目大意:
两人’Y’, 'X’从各自家出发, 前往同一家奶茶店‘@’, “#”表示墙, 求两人到达奶茶店的最小步数和(可走上下左右四个方向, 地图上每走一步,人需要走11步)。
思路
对每个人搜索一次, 数组 w[i][j] 记录两人到达@的步数和, 最后扫一遍数组,找到最小值即可。
一个小细节: 题目说至少有一组解, 说明每个人都可以走到对方家,则,Y能到达的奶茶店,X也能到达, 反之,亦然。

代码亮点

  1. 输入: 直接找到两人位置, 找到奶茶店数量。
  2. 找到奶茶店数量,进行bfs时,进行剪枝操作(使while循环早点结束)
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int N = 210;
int n, m, num;
char str[N][N];
int book[N][N];
int w[N][N];
int d[4][2] = {-1, 0, 0, 1, 1, 0, 0, -1};
struct pp{
    int x, y, step;
};
queue <pp> que;

void bfs(int x, int y){
    while(que.size()) que.pop();
    memset(book, 0, sizeof(book));
    struct pp q;
    int numm = 0;
    q.x = x, q.y = y, q.step = 0;
    que.push(q);
    while(!que.empty()){
        q = que.front();
        que.pop();
        struct pp q2;
        for(int i = 0; i < 4; i ++){
            q2.x = q.x + d[i][0];
            q2.y = q.y + d[i][1];
            q2.step = q.step + 1;
            if(q2.x < 0 || str[q2.x][q2.y] == '#' || q2.x >= n || q2.y < 0 || q2.y >= m || book[q2.x][q2.y] == 1) continue;
            book[q2.x][q2.y] = 1;
            que.push(q2);
            if(str[q2.x][q2.y] == '@'){
                numm ++;
                w[q2.x][q2.y] += q2.step * 11;
                if(numm == num) break;
            }
        }
    }
}

int main(void){
    while(scanf("%d%d", &n, &m) != EOF){
        memset(w, 0, sizeof(w));
        int yx, yy, mx, my;
        num = 0;
        for(int i = 0; i < n; i ++){
            scanf("%s", str[i]);
            for(int j = 0; j < m; j ++){
                if(str[i][j] == 'Y') yx = i, yy = j;
                else if(str[i][j] == 'M') mx = i, my = j;
                else if(str[i][j] == '@') num ++;
            }
        }
        bfs(yx, yy);
        bfs(mx, my);
        int ans = 0x3f3f3f3f;
        for(int i = 0; i < n; i ++){
            for(int j = 0; j < m; j ++){
                if(w[i][j] == 0) continue;
                ans = min(ans, w[i][j]);
            }
        }
        printf("%d\n", ans);
    }
}

最短路

注意点

  1. 注意所求点时谁,曾记否:青蛙在编号为2的点,自己wa了好几回,才发现。
  2. 明确自己想要求得问题,进而找解决的方法,套模板时,要紧紧关注数组含义,更新时得条件,赋值等。

细节题1

N头母牛在N个牛场,都要前往编号为X的牛场,共m条路,都为单向路,每头牛都会走用时最短的那条路。求用时最长的那头牛花费的时间。

细节(敲重点)

  1. 我们不能用floyd,因为数据量较大,时间超时
  2. 我们选择Dijkstra算法,我们求某点k到n,还得求n到k的最短距离,这里我们便用到一个逆矩阵的思想。如我们从 k到 x 需要 g[k][x]的距离,我们把g[x][k]等于该值,这时我们便可转化为求x到k点的最短距离。(可能不太好理解:从k点经过 g点 到 x点的距离,这时我们把g到k点的距离d3 = k到g点的距离d1 , x到g点的距离d4 = g点到x点的距离d2, 那么我们的g点走到x点的距离 = x点到g点的距离)
    我们必须得把整个数组都逆一下,这样才能保证我们用到的路径都是一个方向的
dijkstra(x);
        for(int i = 1; i <= n; i ++){
            for(int j = i + 1; j <= n;j ++){
                int tmp = g[i][j];
                //g[i][j] = g[j][i];
                g[i][j] = g[j][i];
                g[j][i] = tmp;

            }
        }
dijkstra2(x);
  1. 我们写的用的dis数组:由于我们求得是某头牛的时间,那么就得用两个数组来记录,一个记录去时花费的最短时间,一个记录回时的最短时间, 只有两者相加最大时,才是我们所求的答案。(痛哭流涕:错因1)
  2. 在写dijkstra函数时,我们有一个细节if(t == -1) break(错因二)
void dijkstra2(int x){
    for(int i = 1; i <= n; i ++){
        dis2[i] = g[x][i];
        book[i] = 0;
    }
    book[x] = 1;
    for(int i = 1; i <= n; i ++){
        int t = -1;
        for(int j = 1; j <= n; j ++){
            if(!book[j] && (t == -1 || dis2[t] > dis2[j])) t = j;
        }
        if(t == -1) break;
        book[t] = 1;
        for(int j = 1; j <= n; j ++) dis2[j] = min(dis2[j], dis2[t] + g[t][j]);
    }

}

Currency Exchange(Bellman-Ford判断正权回路)

题目描述:
多种汇币,汇币之间可以交换,需要手续费。如:当你用100A币交换B币时,A到B的汇率是29.75,手续费是0.39,那么你可以得到(100 - 0.39) * 29.75 = 2963.3975 B币, 问S币的金额经过交换最终得到的S币金额能否增加。
输入:n种货币,m个交换点,nick拥有的货币种类S, nick拥有的货币数量v。

分析

  1. 货币的交换是可以重复多次的,所以我们需要找出是否存在正权回路正权回路:在这一回路上,顶点的权值能不断增加, 即能一直进行松弛
  2. 一种货币就是一个点,一个“兑换点”就是图上两种货币之间的一个兑换方式,是双边,但A到B的汇率和手续费与B到A的可能不同。
  3. 我们只能在两个货币间进行交换,两个货币间可能交换一次,两次甚至多次,但也有可能0次。不存在“绕弯”(A <–> B <-- > C, 但我们不能AC间直接交换),
  4. 所以我们需要一个while循环,让能让数值变大的两者间一直交换(多换几回),若是不能再变大时,且未超过初始值,我们跳出循环,
#include <iostream>
#include <cstring>
using namespace std;
struct node
{
 int a,b;
 double r,c;
}mp[250];
int n,m,s,all,a,b;
double v,dis[250];
double rab,cab,rba,cba;
int bell()
{
    int i,j;
   memset(dis,0,sizeof(dis));
   dis[s]=v;
   while(dis[s]<=v)//一直处理到钱变多为止
   {
       int flag=0;
       for(j=0;j<all;j++)
       {
           if(dis[mp[j].b]<(dis[mp[j].a]-mp[j].c)*mp[j].r)//肯定从s点开始
           {
               dis[mp[j].b]=(dis[mp[j].a]-mp[j].c)*mp[j].r;//不管跟起始点有无关系只要兑换完了面值增大就兑换,等处理到该点时该点则是最大面值
                flag=1;
           }
 
 
       }
       if(!flag)//都是越兑越小就不换
        	return 0;
   }
   return 1;
 
 
}
int main()
{  int i,j;
    while(cin>>n>>m>>s>>v)
    {all=0;
    for(i=1;i<=m;i++)
    {
       cin>>a>>b>>rab>>cab>>rba>>cba;
       mp[all].a=a;
       mp[all].b=b;
       mp[all].r=rab;
       mp[all++].c=cab;
       mp[all].a=b;
       mp[all].b=a;
       mp[all].r=rba;
       mp[all++].c=cba;
    }
    if(bell())
        cout<<"YES"<<endl;
    else cout<<"NO"<<endl;}
    return 0;
}

Bellman_Ford判断负权回路

题目大意:
T组数据, 每组n个农场, m条正权边, w条负权边, 给出m + w个边的起始点s和终止点e和花费时间t,求是否可能走完任意条边时可以使时间回到最开始之前。

基本内容补充

  1. 回路包括:
    正权回路:回路权值之和为正
    负权回路: 回路权值之和为负
    如果最短路径种包含正权回路,那么去掉这个回路,一定可以得到更短的路径。如果最短路径中包含负权回路, 那么必然无最短路径
    因为每多走一次负权回路就可以得到更短的路径. 因此最短路径肯定是一个不包含回路的最短路径,即最多包含n-1条边。
  2. 我们在 n - 1次松弛后, 如果依然可以继续松弛成功,那么必然存在负环。
    关键代码
for(k=1;k<=n-1;k++)  //外循环循环n-1次,n为顶点个数
    for(i=1;i<=m;i++)//内循环循环m次,m为边的个数,即枚举每一条边
        if(dis[v[i]]>dis[u[i]]+w[i])//尝试对每一条边进行松弛,与Dijkstra算法相同
            dis[v[i]]=dis[u[i]]+w[i]; 
//检测负权回路
flag=0;
for(i=1;i<=m;i++)
    if(dis[v[i]]>dis[u[i]]+w[i])
        flag=1;
if(flag==1)
    printf("此图有负权回路");

【仔细看如下代码:对bellman_frod函数里的循环有个优化,即不一定要进行n-1次循环】

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 5500;
int k; //记录一共有多少有向边
typedef struct edge{
    int u, v; //起点u, 终点v
    int w;
}Edge;
Edge edge[N]; //保存边的值
int dis[N]; //结点道原点的最小距离
int n, m, w, s;

int bellman_ford(){
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    int check = 0;
    for(int i = 1; i <= n - 1; i ++){
        check = 0;
        for(int j = 1; j < k; j ++){
            if(dis[edge[j].v] > dis[edge[j].u] + edge[j].w){
                check = 1;
                dis[edge[j].v] = dis[edge[j].u] + edge[j].w;
            }
        }
        if(check == 0) break; //优化,因为最短路至多为n条边, 但有可能不到n
    }
    int flag = 1;
    for(int i = 1; i < k; i ++){
        if(dis[edge[i].v] > dis[edge[i].u] + edge[i].w){
            flag = 0;
            break;
        }
    }
    return flag;
}

int main(void){
    int t;
    scanf("%d", &t);
    int s, e, v;
    while(t --){
        scanf("%d%d%d", &n, &m, &w);
        k = 1;
        for(int i = 1; i <= m; i ++){
            scanf("%d%d%d", &s, &e, &v);
            edge[k].u = s, edge[k].v = e, edge[k ++].w = v;
            edge[k].u = e, edge[k].v = s, edge[k ++].w = v;
        }
        for(int i = 1; i <= w; i ++){
            scanf("%d%d%d", &s, &e, &v);
            edge[k].u = s, edge[k].v = e, edge[k ++].w = -v;
        }
        int ans = bellman_ford();
        if(ans) cout << "NO" << endl;
        else cout << "YES" << endl;
    }
    return 0;
}

一个新鲜的输入问题

简单描述:
地图上有n个点, 第一行输入n。输入的其他部分定义了一个邻接矩阵a(大小 n * n),每个条目都将是一个整数或字符 x, a(i, j)表示的是从结点i 向 结点 j 发送消息的费用, j向i的费用与其相同; a(i, i) = 0, 所以我们只输入a的下三角形部分即可(因为对称)。求第一个点向其他点传入最短时间的最大值。
主要见输入的如下处理:

atoi 的应用。该代码对应第一个点的坐标是 0, 0,即对应dis[0]

while(scanf("%d", &n) != EOF){
        char s[3];
        for(int i = 0; i < n; i ++){
            g[i][i] = 0;
            for(int j = 0; j < i; j ++){
               cin >> s;
                if(s[0] != 'x') g[i][j] = g[j][i] = atoi(s);
                else g[i][j] = g[j][i] = 0x3f3f3f3f;
            }
        }
        cout << dijkstra() << endl;
    }

Floyd 的包装1

题目大意: n头牛比赛, 给出m场两两比赛获胜方,问有几头牛可以确定名次(A打败B, B打败C, 则A打败C),

将标记数组book改为计数数组, 当book[i] = k , 即认为 第i头牛打了k场比赛, 当k = n - 1 时,我们即可确定这头牛的名次

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 105;
int dis[N], book[N];
int g[N][N];
int n, m;
int main(void){
    while(scanf("%d%d", &n, &m) != EOF){
        memset(book, 0, sizeof(book));
        memset(g, 0, sizeof(g));
        for(int i = 1; i <= m; i ++){
            int a, b;
            scanf("%d%d", &a, &b);
            g[a][b] = 1;
            book[a] ++;
            book[b] ++;
        }
        int num = 0;
        for(int i = 1; i <= n; i ++){
            for(int j = 1; j <= n; j ++){
                for(int k = 1; k <= n; k ++){
                    if(i == j || j == k || i == k) continue;
                    if(!g[j][k] && g[j][i] && g[i][k]){
                        g[j][k] = 1;
                        book[j] ++;
                        book[k] ++;
                    }
                }
            }
        }
        for(int i = 1; i <= n; i ++){
            if(book[i] == n - 1) num ++;
        }
        cout << num << endl;
    }
    return 0;
}

二分

跳石头(贪心+二分转换)

题目大意:
组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。
输入:
第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。
接下来 N 行,每行一个整数,第 i 行的整数 Di(0<Di<L)表示第 i 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同 一个位置。
输出:
输出文件只包含一个整数,即最短跳跃距离的最大值。

思路

  1. 求移走M块石头后最短距离的最大值。贪心的 策略:找到一个dist, 使移走的的最短距离的最小值不大于dist。
  2. 当我们的中间值mid满足check时,试着往右找最大值,所以变化的区间为l = mid
  3. 当中间值不满足check时,说明这个值太大了,挪走的石头数量比题意多,自然要r = mid - 1
  4. 细节 mid = (l + r + 1) / 2
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 50010;
long long arr[N], l, m, n;

bool check(long long mid){
    long long res = 0, last = 0;
    for(int i = 1; i <= n; i ++){
        if(arr[i] - last < mid) res ++; //当小于mid时,我们挪走该石头,此时last并未改变
        else last = arr[i];
    }
    return res <= m;
}

int main(void){
    scanf("%lld%lld%lld", &l, &n, &m);
    for(int i = 1; i <= n; i ++) scanf("%lld", &arr[i]);
    arr[++ n] = l;
    l = 0;
    long long r = 1e9;
    while(l < r){
        long long mid = (l + r + 1) / 2;
        if(check(mid)) l = mid;
        else r = mid - 1;
    }
    cout << l << endl;
    return 0;

}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Xuhx&

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

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

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

打赏作者

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

抵扣说明:

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

余额充值