[OI][杂题]NO.1

NOIP 2015 跳石头

题目背景

一年一度的“跳石头”比赛又要开始了!

题目描述

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终 点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达 终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳 跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能 移走起点和终点的岩石)。

输入输出格式

输入格式:

输入文件名为 stone.in。

输入文件第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终 点之间的岩石数,以及组委会至多移走的岩石数。

接下来 N 行,每行一个整数,第 i 行的整数 Di(0 < Di < L)表示第 i 块岩石与 起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同 一个位置。

输出格式:

输出文件名为 stone.out。 输出文件只包含一个整数,即最短跳跃距离的最大值。

输入输出样例

输入样例#1:
25 5 2 
2
11
14
17 
21
输出样例#1:
4

说明

输入输出样例 1 说明:将与起点距离为 2 和 14 的两个岩石移走后,最短的跳跃距离为 4(从与起点距离 17 的岩石跳到距离 21 的岩石,或者从距离 21 的岩石跳到终点)。

另:对于 20%的数据,0 ≤ M ≤ N ≤ 10。 对于50%的数据,0 ≤ M ≤ N ≤ 100。

对于 100%的数据,0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000。

------

思路

最短的最长,显然用二分答案

初始区间[0, L+1),当跳跃的最短距离设为0的时候,即选手们至少跳0,显然不需要移走任何一个石头,因此0可以视为合法的;当跳跃的最短距离设为L+1的时候,显然选手是无论如何都跳不到这么长的,因此L+1视为不合法。

因此区间是左闭右开,表示左端点一直是合法的,右端点一直非法的

当前区间[L,R),二分答案Ans

Ans表示选手跳跃的最短距离

贪心思想:从前往后扫描石头,模拟选手跳跃的过程,如果能跳(距离大于等于Ans),就跳;不能跳,只能将该石头搬走

最后总共搬走的石头数如果小于等于M,表示合法,区间变成[Ans,R);否则区间变成[L,Ans)

直到区间内没有数了,答案就是左端点。

code

/*只要最短距离确定了,因为两端是定的,所以可以确定必须搬走的石块,因为要求最小值,所以只要把必须搬走的搬走就好了,其他的随意

所以只需要O(n)枚举一次,使得前i个石头确定不搬,那么需要确定第i+1块石头怎么办,显然,一旦两者距离大于了那个最小值就必须把他搬走

所以,时间复杂度是O(nlog(L))*/

#include <cstdio>
#include <cstring>
#define maxn 50005

using namespace std;

int a[maxn],n,m,ans;

bool check(int x) {
    int sum,last;
    sum = 0; last = 0;
    for (int i = 1; i <= n; i++) {
        //能跳就跳,跳不了就移 
        if (a[i] - last < x) {//如果当前石子减去前一个石子(即两石子间的距离)小于所cheak的跳跃的最短距离
            sum++;//那么我们就只能将这个石子移走 
            continue;
        }
        last = a[i];//更新last 
    }
    if (sum > m) return false;//如果需要移走的石子总数大于题目最大值
    //假的吧~~ 
    return true; //要不就是真的 
}

int main() {
    int L,R,mid;
    scanf("%d%d%d",&L,&n,&m);
    for (int i = 1; i <= n; i++) 
        scanf("%d",&a[i]);
    n++;
    a[n] = L; R = L; ans = L = 0; 
    while (L <= R) {   
        mid = (L + R) / 2; //好好(瞎)找中点。 
        if (check(mid)) { //二分
            ans = mid; 
            L = mid + 1; //左端点更新 (这里与下边右端点的更新都是答案,也就是最短距离的更新) 
        }
        else R = mid - 1; //右端点更新 
    }
    printf("%d",ans);
    return 0;
}

[洛谷] P1736 创意吃鱼法

题目描述

回到家中的猫猫把三桶鱼全部转移到了她那长方形大池子中,然后开始思考:到底要以何种方法吃鱼呢(猫猫就是这么可爱,吃鱼也要想好吃法 ^_*)。她发现,把大池子视为01矩阵(0表示对应位置无鱼,1表示对应位置有鱼)有助于决定吃鱼策略。

在代表池子的01矩阵中,有很多的正方形子矩阵,如果某个正方形子矩阵的某条对角线上都有鱼,且此正方形子矩阵的其他地方无鱼,猫猫就可以从这个正方形子矩阵“对角线的一端”下口,只一吸,就能把对角线上的那一队鲜鱼吸入口中。

猫猫是个贪婪的家伙,所以她想一口吃掉尽量多的鱼。请你帮猫猫计算一下,她一口下去,最多可以吃掉多少条鱼?

输入输出格式

输入格式:

有多组输入数据,每组数据:

第一行有两个整数n和m(n,m≥1),描述池塘规模。接下来的n行,每行有m个数字(非“0”即“1”)。每两个数字之间用空格隔开。

对于30%的数据,有n,m≤100

对于60%的数据,有n,m≤1000

对于100%的数据,有n,m≤2500

输出格式:

只有一个整数——猫猫一口下去可以吃掉的鱼的数量,占一行,行末有回车。

输入输出样例

输入样例#1: 

4 6 0 1 0 1 0 0

0 0 1 0 1 0

1 1 0 0 0 1

0 1 1 0 1 0

输出样例#1:

3 说明

右上角的

1 0 0 0 1 0 0 0 1

code

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>

using namespace std;

inline int read() {
    int num = 0 , f = 1;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '0') f = -1;
        ch = getchar();
    }
    while (isdigit(ch)) {
        num = num * 10 + ch - '0';
        ch = getchar();
    }
    return num * f;
}

int n,m,ans;
int map[2510][2510],f[2510][2510],sum1[2510][2510],sum2[2510][2510];

int main() {
    n = read(); m = read();
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            map[i][j] = read();
            if (!map[i][j]) {  
                sum1[i][j] = sum1[i - 1][j] + 1;
                sum2[i][j] = sum2[i][j - 1] + 1;
            }
            else 
                f[i][j] = min(f[i - 1][j - 1],min(sum1[i - 1][j],sum2[i][j - 1])) + 1;
            ans = max(ans,f[i][j]);
        }
    memset(f,0,sizeof(f)); 
    memset(sum1,0,sizeof(sum1)); 
    memset(sum2,0,sizeof(sum2));
    for (int i = 1; i <= n; i++)
        for (int j = m; j >= 1; j--) {
            if (!map[i][j]) {
                sum1[i][j] = sum1[i - 1][j] + 1;
                sum2[i][j] = sum2[i][j + 1] + 1;
            }
            else 
                f[i][j] = min(f[i - 1][j + 1],min(sum1[i - 1][j],sum2[i][j + 1])) + 1;
            ans = max(ans,f[i][j]);
        }
    printf("%d",ans);
    return 0;
}

[ZJOI2005] 午餐

题目描述

上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂。这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭。由于每个人的口味(以及胃口)不同,所以他们要吃的菜各有不同,打饭所要花费的时间是因人而异的。另外每个人吃饭的速度也不尽相同,所以吃饭花费的时间也是可能有所不同的。

THU ACM小组的吃饭计划是这样的:先把所有的人分成两队,并安排好每队中各人的排列顺序,然后一号队伍到一号窗口去排队打饭,二号队伍到二号窗口去排队打饭。每个人打完饭后立刻开始吃,所有人都吃完饭后立刻集合去六教地下室进行下午的训练。

现在给定了每个人的打饭时间和吃饭时间,要求安排一种最佳的分队和排队方案使得所有人都吃完饭的时间尽量早。

假设THU ACM小组在时刻0到达十食堂,而且食堂里面没有其他吃饭的同学(只有打饭的师傅)。每个人必须而且只能被分在一个队伍里。两个窗口是并行操作互不影响的,而且每个人打饭的时间是和窗口无关的,打完饭之后立刻就开始吃饭,中间没有延迟。

现在给定N个人各自的打饭时间和吃饭时间,要求输出最佳方案下所有人吃完饭的时刻。

输入输出格式

输入格式:

第一行一个整数N,代表总共有N个人。

以下N行,每行两个整数 Ai,Bi。依次代表第i个人的打饭时间和吃饭时间。

输出格式:

一个整数T,代表所有人吃完饭的最早时刻。

输入输出样例

输入样例#1: 
5
2 2
7 7
1 3
6 4
8 5
输出样例#1: 
17

说明

所有输入数据均为不超过200的正整数。

 ------

思路

为了简化问题,我们先来考虑一对怎么搞

首先,我们需要一个顺序。也就是划分阶段

类似于排队接水的顺序,但需要换一个角度思考

由于每个人打完饭就跑路开吃,所以所有人总共要打饭的时间是固定的,就是把所有人的打饭时间加起来

那么我们考虑,如果仅有两人AB,他们的打饭时间和吃饭时间都不同,是A在前快还是B在前快?

画个图就知道,很明显是吃饭时间长的排在前面比较快。

for(int i = 1; i <= n; ++ i)
    cin >> a[i] >> b[i], p[i] = i;
sort(p+1, p+n+1, cmp); // 按照吃饭时间排序

得到这个结论后,就可以搞DP了

对于每个人,我们考虑是去第一队还是第二队两种情况

用f[i]表示考虑到第i 个人, 目前最优的吃饭时间

状态补充一下,用f[i][j][k]表示第一条队总时长为j, 第二条队总时长为k

到这里DP就差不多了

但是我们发现

三维数组造成了很大的空间开销

能不能数组压缩为二维数组呢?

在考虑顺序时我们说过,总的排队时间是不变的

也就是说,如果知道在一队的全部人,那么我们也就知道了在二队的全部人,因为二队人的排队时间和等于总和减去一队人的排队时间和

f[i+1][j+A] = min(f[i+1][j+A], max(f[i][j], j+A+B)); // 排第一条队
f[i+1][j] = min(f[i+1][j], max(f[i][j], s[i-1]-j+A+B)); // 排第二条队

只需要在DP前搞一下前缀和就好了

for(int i = 1; i <= n; ++ i) // 求前缀和
    s[i] = s[i-1] + a[p[i]];

这样我们就可以用二维数组解决掉这道题

当然你也可以用两个不同的数组表示一队和二队

这里不再赘述。

code

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 205;
const int M = N * N;
const int INF = 1e9;

int n, f[N][M], sum[N]; 

struct Edge {
    int A, B;
}T[N];

bool cmp(Edge x, Edge y) { return x.B > y.B;}

int main() {
    scanf("%d", &n); 
    for (int i = 1; i <= n; i++) 
        scanf("%d%d",&T[i].A, &T[i].B);
    sort(T + 1, T + n + 1, cmp);
    for (int i = 1; i <= n; i++) 
        sum[i] = sum[i-1] + T[i].A;
    int m = n * N;
    for(int i = 1; i <= n + 1; i++) 
        for(int j = 0; j <= m; j++) f[i][j] = INF;
    f[1][0] = 0; 
    for (int i = 1; i <= n; i++) 
        for (int j = 0; j <= m; j++) 
            if (f[i][j] < INF) {
                f[i+1][j+T[i].A] = min(f[i+1][j+T[i].A], 
            max(f[i][j], j + T[i].A + T[i].B));
                f[i+1][j] = min(f[i+1][j], 
            max(f[i][j], sum[i-1] - j + T[i].A + T[i].B));
            }
    int ans = INF;
    for(int i = 0; i <= m; i++) ans = min(ans, f[n+1][i]);
    printf("%d", ans);
    return 0;
}

P3701 「伪模板」主席树

题目背景

byx和手气君都非常都非常喜欢种树。有一天,他们得到了两颗奇怪的树种,于是各自取了一颗回家种树,并约定几年后比一比谁种出来的树更加牛x。

题目描述

很快,这棵树就开花结果了。byx和手气君惊讶的发现,这是一棵主席树,树上长满了主席和主席的朋友们。这棵树上一共有五种人,主席(J),记者(HK),高人(W),女王(E)和膜法师(YYY)。他们发现,他们的主席树上的人数相同,都为N。

研究发现,这五种人的输赢如上图所示(一样的人不能PK),箭头指向输的人。至于为什么,留给同学们自己思考。

比赛如期进行。

byx和手气君要进行M场比赛,每一场比赛他们会选出树上的两个人来比较看谁更牛x。

第i个人寿命为Lifei秒,每次比完赛他们就会-1s。当他们生命为0s时他们就不能再比赛了。

同时,当J的寿命为0时,同一棵树上的YYY可以为他+1s。每个YYY只能给.每个J续一次。

那么问题来了

现在给定N,M(1≤N≤100,1≤M≤1000),A和B每一个人所属种类(J,HK,W,YYY或E)以及每一个人的生命,生命不超过50.请你算算A最多能够赢得多少场比赛呢。

数据保证每一场一定都有人用。两个人之间只能比一场。

输入输出格式

输入格式:

第一行包含两个数N,M,含义看上面。

第二行N个字串(J,HK,W,YYY或E),表示byx的人所属种类,用空格隔开。

第三行N个字串(J,HK,W,YYY或E),表示手气君的人所属种类,用空格隔开。

第四行N个数,表示byx的人的生命。

第五行N个数,表示手气君的人的生命。

输出格式:

一个数,byx能赢的场次

输入输出样例

输入样例#1: 
3 3
J W YYY
J HK E
2 2 2
2 2 2
输出样例#1: 
3

说明

第一场主席赢记者,第二场高人赢女王,第三场膜法师赢记者。

------

思路

标题都是骗人的

本来还以为是主席树……看了题面发现是网络流……

觉得发现了一道极水的黑题

但是调了2个多小时……

为什么呢?

    for (R int i = 1 + n; i <= 2 * n; ++ i) 
        AddEdge(i, T, hp2[i]), AddEdge(T, i, 0);
    for (R int i = 1; i <= n; ++ i) 
        AddEdge(i + n, T, hp2[i]), AddEdge(T, i + n, 0);

这两句……有区别吗?

但是第一个就是输出0啊

R是register……去掉也不管用……

为啥?为啥?为啥?

这种玄学错误我还能调出来真的是谢天谢地了

求大佬解惑

code

#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#define R register

typedef long long ll;
typedef double db;
const int INF = 1e9;
const int MAXM = 10010;
int n, m, hp1[110], hp2[110];
char name1[110][3], name2[110][3];
int ecnt = 1, head[MAXM], dep[MAXM];

struct Edge {
    int to, nxt, ret;
}e[MAXM];

inline int read() {
    int num = 0, f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {num = num * 10 + ch - '0'; ch = getchar();}
    return num * f;
}

void AddEdge(int x, int y, int c) {
    e[++ ecnt].to = y; e[ecnt].ret = c; 
    e[ecnt].nxt = head[x]; head[x] = ecnt;
}

int dfs(int x, int t, int maxFlow) {
    if (!maxFlow || x == t) return maxFlow;
    int ans = 0, v, f;
    for (int i = head[x]; i; i = e[i].nxt) {
        v = e[i].to;
        if (dep[v] == dep[x] + 1 && e[i].ret > 0) {
            f = dfs(v, t, std::min(maxFlow - ans, e[i].ret));
            e[i].ret -= f;
            e[i ^ 1].ret += f;
            ans += f;
        }
    }
    if (ans < maxFlow) dep[x] = n + 1;
    return ans;
}

bool bfs(int s, int t) {
    std::queue<int> q;
    memset(dep, -1, sizeof(dep));
    q.push(s);
    dep[s] = 0;
    int h, v;
    while (!q.empty()) {
        h = q.front(); q.pop();
        for (int i = head[h]; i; i = e[i].nxt) {
            v = e[i].to;
            if (e[i].ret > 0 && dep[v] == -1) {
                q.push(v);
                dep[v] = dep[h] + 1;
            }
        }
    } return dep[t] != -1;
}

int Dinic(int s, int t) {
    int ans = 0;
    while (bfs(s, t)) ans += dfs(s, t, INF);
    return ans;
}

int main() {
    n = read(), m = read();
    //std::cin >> n >> m;
    int add1 = 0, add2 = 0;
    for (R int i = 1; i <= n; ++ i) {
        scanf("%s", name1[i]);
        if (name1[i][0] == 'Y') ++ add1;
    }
    for (R int i = 1; i <= n; ++ i) {
        scanf("%s", name2[i]);
        if (name2[i][0] == 'Y') ++ add2;
    }
    for (R int i = 1; i <= n; ++ i) {
        hp1[i] = read();
        //std::cin >> hp1[i];
        if (name1[i][0] == 'J') hp1[i] += add1;
    }
    for (R int i = 1; i <= n; ++ i) {
        hp2[i] = read();
        //std::cin >> hp2[i];
        if (name2[i][0] == 'J') hp2[i] += add2;
    }
    int S = 0, T = 2 * n + 1;
    for (R int i = 1; i <= n; ++ i) 
        AddEdge(S, i, hp1[i]), AddEdge(i, S, 0);
    /*for (R int i = 1 + n; i <= 2 * n; ++ i) 
        AddEdge(i, T, hp2[i]), AddEdge(T, i, 0);*/
    for (R int i = 1; i <= n; ++ i) 
        AddEdge(i + n, T, hp2[i]), AddEdge(T, i + n, 0);
    for (R int i = 1; i <= n; ++ i) for (R int j = 1; j <= n; ++ j) {
        if (name1[i][0] == 'J' && (name2[j][0] == 'W' || name2[j][0] == 'H'))
            AddEdge(i, j + n, 1), AddEdge(j + n, i, 0);
        if (name1[i][0] == 'W' && (name2[j][0] == 'Y' || name2[j][0] == 'E'))
            AddEdge(i, j + n, 1), AddEdge(j + n, i, 0);
        if (name1[i][0] == 'H' && (name2[j][0] == 'W' || name2[j][0] == 'E'))
            AddEdge(i, j + n, 1), AddEdge(j + n, i, 0);
        if (name1[i][0] == 'E' && (name2[j][0] == 'Y' || name2[j][0] == 'J'))
            AddEdge(i, j + n, 1), AddEdge(j + n, i, 0);
        if (name1[i][0] == 'Y' && (name2[j][0] == 'H' || name2[j][0] == 'J'))
            AddEdge(i, j + n, 1), AddEdge(j + n, i, 0);
    }    
    std::cout << std::min(Dinic(S, T), m) << std::endl;
    return 0;
}

QwQ无力吐槽

P3431 [POI2005]AUT-The Bus

题目描述

The streets of Byte City form a regular, chessboardlike network - they are either north-south or west-east directed. We shall call them NS- and WE-streets. Furthermore, each street crosses the whole city. Every NS-street intersects every WE- one and vice versa. The NS-streets are numbered from 111 to nnn , starting from the westernmost. The WE-streets are numbered from 111 to mmm , beginning with the southernmost. Each intersection of the iii 'th NS-street with the jjj 'th WE-street is denoted by a pair of numbers (i,j)(i,j)(i,j) (for 1≤i≤n1\le i\le n1in , 1≤j≤m1\le j\le m1jm ).

There is a bus line in Byte City, with intersections serving as bus stops. The bus begins its itinerary by the (1,1)(1,1)(1,1) intersection, and finishes by the (n,m)(n,m)(n,m) intersection. Moreover, the bus may only travel in the eastern and/or northern direction.

There are passengers awaiting the bus by some of the intersections. The bus driver wants to choose his route in a way that allows him to take as many of them as possible. (We shall make an assumption that the interior of the bus is spacious enough to take all of the awaiting passengers, regardless of the route chosen.)TaskWrite a programme which:

reads from the standard input a description of the road network and the number of passengers waiting at each intersection,finds, how many passengers the bus can take at the most,writes the outcome to the standard output.

Byte City 的街道形成了一个标准的棋盘网络 – 他们要么是北南走向要么就是西东走向. 北南走向的路口从 1 到 n编号, 西东走向的路从1 到 m编号. 每个路口用两个数(i, j) 表示(1 <= i <= n, 1 <= j <= m). Byte City里有一条公交线, 在某一些路口设置了公交站点. 公交车从 (1, 1) 发车, 在(n, m)结束.公交车只能往北或往东走. 现在有一些乘客在某些站点等车. 公交车司机希望在路线中能接到尽量多的乘客.帮他想想怎么才能接到最多的乘客.

输入输出格式

输入格式:

The first line of the standard input contains three positive integers nnn , mmm and kkk - denoting the number of NS-streets, the number of WE-streets and the number of intersections by which the passengers await the bus, respectively ( 1≤n≤1091\le n\le 10^91n109 , 1≤m≤1091\le m\le 10^91m109 , 1≤k≤1051\le k\le 10^51k105 ).

The following kkk lines describe the deployment of passengers awaiting the bus, a single line per intersection. In the (i+1)(i+1)(i+1) 'st line there are three positive integers xix_ixi , yiy_iyi and pip_ipi , separated by single spaces, 1≤xi≤n1\le x_i\le n1xin , 1≤yi≤m1\le y_i\le m1yim , 1≤pi≤1061\le p_i\le 10^61pi106 . A triplet of this form signifies that by the intersection (xi,yi)pi(x_i,y_i)p_i(xi,yi)pi​ passengers await the bus. Each intersection is described in the input data once at the most. The total number of passengers waiting for the bus does not exceed 1 000 000 0001\ 000\ 000\ 0001 000 000 000 .

输出格式:

Your programme should write to the standard output one line containing a single integer - the greatest number of passengers the bus can take.

输入输出样例

输入样例#1: 
8 7 11
4 3 4
6 2 4
2 3 2
5 6 1
2 5 2
1 5 5
2 1 1
3 1 1
7 7 1
7 4 2
8 6 2
输出样例#1: 

11

------

日常DP

code

#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <vector>
#define R register

const int MAXN = 100010;

int n, m, k;
int b[MAXN], f[MAXN], ans, cnt;

struct POS{
    int x, y, num;
    bool operator < (const POS &t) const {
        if (x == t.x) return y < t.y;
        return x < t.x;
    }
}p[MAXN];

inline int read() {
    int num = 0, f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {num = num * 10 + ch - '0'; ch = getchar();}
    return num * f;
}

int t[MAXN];
int Lowbit(int x) {return x&(-x);}
void Update(int x, int k) {while(x<=cnt)t[x]=std::max(t[x],k),x+=Lowbit(x);}
int Query(int x) {int d=0;while(x>0)d=std::max(d,t[x]),x-=Lowbit(x);return d;}

int main() {
    n = read(); m = read(); k = read();
    for (R int i = 1; i <= k; ++ i) {
        p[i].x = read(), p[i].y = read(), p[i].num = read();
        b[i] = p[i].y;
    }
    std::sort(b + 1, b + k + 1);
    cnt = std::unique(b + 1, b + k + 1) - (b + 1);
    for (R int i = 1; i <= k; ++ i) 
        p[i].y = std::lower_bound(b + 1, b + cnt + 1, p[i].y) - b;
    std::sort(p + 1, p + k + 1);
    for (int i = 1; i <= k; ++ i) {
        f[i] = Query(p[i].y) + p[i].num;
        ans = std::max(f[i], ans);
        Update(p[i].y, f[i]);
    }
    std::cout << ans << std::endl;
}

猜你喜欢

转载自www.cnblogs.com/hkttg/p/10162543.html