蓝桥杯国赛五一训练赛(1)

蓝桥杯国赛五一训练赛(1)(链接)

问题 A: 费解的开关

你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。
我们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态
10111
01101
10111
10000
11011
在改变了最左上角的灯的状态后将变成:
01111
11101
10111
10000
11011
再改变它正中间的灯后状态将变成:
01111
11001
11001
10100
11011
给定一些游戏的初始状态,编写程序判断游戏者是否可能在6步以内使所有的灯都变亮。
输入
第一行有一个正整数n,代表数据中共有n个待解决的游戏初始状态。
以下若干行数据分为n组,每组数据有5行,每行5个字符。每组数据描述了一个游戏的初始状态。各组数据间用一个空行分隔。
对于30%的数据,n<=5;
对于100%的数据,n<=500。
输出
输出数据一共有n行,每行有一个小于等于6的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。
对于某一个游戏初始状态,若6步以内无法使所有灯变亮,请输出“-1”。

样例输入

3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111

样例输出:

3
2
-1

根据题意可推知,我们通过按下一排的开关控制上一排的开关状态,这样最终只需要检查最后一排灯是否都开着,因此第一排灯的初始开关状态能确定最终确定最后的状态,因此我们只需枚举2^5=32种第一排的初始状态就行。

AC代码:

#include 
using namespace std;
typedef long long int ll;
string a[5];
string tp[5];
int dir[]={1,0,-1,0,1};
void switch_(int x,int y){  //转换(x,y)灯及其四周灯状态
    tp[x][y]^=1;
    for(int i=0;i<4;i++){
        int tx=x+dir[i];
        int ty=y+dir[i+1];
        if(tx>=0&&ty>=0&&tx<5&&ty<5) tp[tx][ty]^=1;
    }
}
bool check(){
    for(int i=0;i<5;i++)
        if(tp[4][i]=='0') return false;
    return true;
}
void solve(){
    int res=7,cnt;
    for(int i=0;i<5;i++) cin>>a[i];
    for(int k=0;k<(1<<5);k++){//枚举第一行的所有按法
        cnt=0;
        for(int i=0;i<5;i++) tp[i]=a[i];//备份
        for(int i=0;i<5;i++)    
            if((1<<i)&k){
                switch_(0,i);
                cnt++;
            }
        for(int i=0;i<4;i++)
            for(int j=0;j<5;j++)
                if(tp[i][j]=='0'){
                    cnt++;
                    switch_(i+1,j);
                }
        if(check()) 
            res=min(res,cnt);
    }
    if(res>6) res=-1;
    printf("%d",res);
}
int main()
{
    int n;
    cin>>n;
    while (n--)
    {
        solve();
        printf("\n");
    }
    return 0;
}
问题 B: 最短Hamilton路径

题目描述
给定一张 n(n≤20) 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。
输入
第一行一个整数n。

接下来n行每行n个整数,其中第i行第j个整数表示点i到j的距离(一个不超过10^7的正整数,记为a[i,j])。

对于任意的x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]>=a[x,z]。

输出
一个整数,表示最短Hamilton路径的长度。
样例输入

4
0 2 1 3
2 0 2 1
1 2 0 1
3 1 1 0

样例输出

4

我们不关心走过点的顺序,只关心当走到某个点j时已经走过的点的集合,因此可以利用二进制数表示已经走过的点的集合,定义状态f[i][j],其中i是二进制状态压缩下的点的集合,f[i][j]表示在经过i中点后走到j点最短路径长度,状态的转移如下:
f[i][j] = f[i-j][k] + mp[k][j](i-j是指从集合i中去掉j,mp[k][j]指k,j的路径长)。

AC代码:

#include 
using namespace std;
typedef long long int ll;
int f[1<<20][20];
//f[i][j] = f[i-j][k] + mp[k][j]
int mp[20][20];
int main()
{
    int n;
    cin>>n;
    memset(f,0x3f,sizeof f);
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin>>mp[i][j];
    f[1][0]=0;  //起点路径长为0
    for(int i=0;i<1<<n;i++){    //枚举所有状态
        for(int j=0;j<n;j++){
            if((1<<j)&i){       //如果j点在i中,更新最短路径
                for(int k=0;k<n;k++){
                    if((1<<k)&(i-(1<<j))){
                        f[i][j]=min(f[i][j],f[i-(1<<j)][k]+mp[k][j]);
                    }
                }
            }
        }
    }
    cout<<f[(1<<n)-1][n-1];
    return 0;
}
问题 C: IncDec Sequence

给定一个长度为n的数列{a1,a2…an},每次可以选择一个区间[l,r],使这个区间内的数都加一或者都减一。
问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。
输入
第一行一个正整数n
接下来n行,每行一个整数,第i+1行的整数表示ai。
输出
第一行输出最少操作次数
第二行输出最终能得到多少种结果
样例输入

4
1
1
2
2

样例输出

1
2

根据题目描述,我们最终想通过最少操作使得原数组差分数组的第2~n位数字都为0(也即是原数组所有数都相等),而题中描述的操作即是每次从差分数组中选两个数,前者加1(减1)后者减1(加1),故我们每次选差分数组中两个符号不同的数进行操作,当只剩下大于0或者小于0的数时,可以选择第1个数或第n+1个数和这些数进行运算(即改变前缀和后缀,而改变前缀后后缀只会影响最终数组中数字的值,不会影响数组所有元素相等),最终使得差分数组除去第一位外的所有数都变成0,操作的总次数应该为差分数组中所有正数的和(记为x)与所有负数的和的绝对值(记为y)的最大值,现在考虑最终可能的结果数,当差分数组中只剩下正数/负数时,只能修改前缀/后缀,不同修改方式对应不同差分数组,最终对应不同的数组,总可能数为abs(x-y)+1种;
例如:1 2 3对应差分数组为1 1 1
而1 2 3=>2 2 3=>3 3 3或1 2 3=>2 2 3=>2 2 2或1 2 3=>1 1 2=>1 1 1,这三种情况均符合题意要求
AC代码:

#include 
using namespace std;
typedef long long int ll;
int main()
{
    int n;
    cin>>n;
    vector<int>a(n);
    for(int &x:a) cin>>x;
    ll x=0,y=0;
    for(int i=n-1;i>0;i--) a[i]-=a[i-1];
    for(int i=1;i<n;i++)
        if(a[i]>0) x+=a[i];
        else y-=a[i];
    if(x>y) swap(x,y);
    cout<<y<<'\n'<<y-x+1;
    return 0;
}
问题 D: Best Cow Fences

题目描述
给定一个长度为n的非负整数序列A,求一个平均数最大的,长度不小于L的子段。
输入
第一行用空格分隔的两个整数n和L;
第二行为n个用空格隔开的非负整数,表示Ai。
输出
输出一个整数,表示答案的1000倍。不用四舍五入,直接输出。
样例输入

10 6
6 4 2 10 3 8 5 9 4 1 

样例输出

6500

思路:题目所求满足二分性质,于是可以二分答案,而对于平均值m来说,可以如下验证m是否可取,首先我们需要求出前缀和,在求前缀和的同时可以减掉m,则问题转化为是否有长度大于等于L的子段和大于等于0,即能否找到i,j使得s[j]-s[i]>=0且j-i>=L
AC代码:

#include 
using namespace std;
typedef long long int ll;
const int N = 1e5+5;
const double eps = 1e-5;
int n,l;
int a[N];
double b[N];
bool check(double m){
    for(int i=1;i<=n;i++) b[i]=b[i-1]+a[i]-m;
    double minx=0;      //minx记录最小的b[i],使得b[j]-b[i]尽可能大
    for(int i=0,j=l;j<=n;i++,j++){
        minx=min(minx,b[i]);
        if(b[j]-minx>=0) return true;
    }
    return false;
}
int main()
{
    cin>>n>>l;
    for(int i=1;i<=n;i++) cin>>a[i];
    double l=0,r=1e6;
    while (l+eps<r)
    {
        double m=(l+r)/2;
        if(check(m)) l=m;
        else r=m;
    }
    cout<<(int)(r*1000);
    return 0;
}
问题 E: SOLDIERS

题目描述
N soldiers of the land Gridland are randomly scattered around the country. A position in Gridland is given by a pair (x,y) of integer coordinates. Soldiers can move - in one move, one soldier can go one unit up, down, left or right (hence, he can change either his x or his y coordinate by 1 or -1). The soldiers want to get into a horizontal line next to each other (so that their final positions are (x,y), (x+1,y), …, (x+N-1,y), for some x and y). Integers x and y, as well as the final order of soldiers along the horizontal line is arbitrary. The goal is to minimise the total number of moves of all the soldiers that takes them into such configuration. Two or more soldiers must never occupy the same position at the same time.
输入
The first line of the input contains the integer N, 1 ≤ N ≤ 10000, the number of soldiers. The following N lines of the input contain initial positions of the soldiers : for each i, 1 ≤ i ≤ N, the (i+1)st line of the input file contains a pair of integers x[i] and y[i] separated by a single blank character, representing the coordinates of the ith soldier, -10000 ≤ x[i],y[i] ≤ 10000.
输出
The first and the only line of the output should contain the minimum total number of moves that takes the soldiers into a horizontal line next to each other.
样例输入

5
1 2
2 2
1 3
3 -2
3 3

样例输出

8

题目大意:给你n个士兵坐标,每个士兵可以沿着坐标轴整数点移动,每往四联通方向任走一格花费一体力,现要求你求出把士兵排成一行花费最小体力(士兵不能重叠)
思路:x,y两个方向是独立的,先处理y,问题等价于找一个点使得所有点到该点距离和最小,那这个点应该是所有点的中位数,这样处理后士兵处于一排,不过可能会有所重叠,假设对x重新排序后士兵的位置为x1-a,x2-(a+1),…,xn-(a+n-1),移项后变为x1-0-a,x2-1-a,…,xn-(n-1)-a,我们需要确定一个a使得xi到a的距离和最小,此时问题已经转化成x轴上的中位数问题
AC代码:

#include 
using namespace std;
typedef long long int ll;
const int N = 1e5+5;
const double eps = 1e-5;
int n;
int x[N],y[N];
int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>x[i]>>y[i];
    sort(x,x+n);
    sort(y,y+n);
    int res=0;
    int md=y[n/2];
    for(int i=0;i<n;i++) {res+=abs(y[i]-md);x[i]-=i;}
    sort(x,x+n);
    md=x[n/2];
    for(int i=0;i<n;i++) res+=abs(x[i]-md);
    cout<<res;
    return 0;
}
问题 F: To the Max

题目描述
Given a two-dimensional array of positive and negative integers, a sub-rectangle is any contiguous sub-array of size 1*1 or greater located within the whole array. The sum of a rectangle is the sum of all the elements in that rectangle. In this problem the sub-rectangle with the largest sum is referred to as the maximal sub-rectangle.
As an example, the maximal sub-rectangle of the array:

0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
is in the lower left corner:

9 2
-4 1
-1 8
and has a sum of 15.
输入
The input consists of an N*N array of integers. The input begins with a single positive integer N on a line by itself, indicating the size of the square two-dimensional array. This is followed by N2 integers separated by whitespace (spaces and newlines). These are the N2 integers of the array, presented in row-major order. That is, all numbers in the first row, left to right, then all numbers in the second row, left to right, etc. N may be as large as 100. The numbers in the array will be in the range [-127,127].
输出
Output the sum of the maximal sub-rectangle.
样例输入

4
0 -2 -7 0
9 2 -6 2
-4 1 -4  1
-1 8  0 -2

样例输出

15

题目大意:给个nxn矩阵,求他的所有子矩阵中元素和最大的子矩阵,输出这个最大的和
思路:如果用二维前缀和加上暴力枚举,时间复杂度在 O ( n 4 ) O(n^4) O(n4),n<=100,总计算量在1e8级别,也许能勉强卡过。如果预先求出每一列的前缀和,再去求出行方向上的最大子段和,时间复杂度能降到 O ( n 3 ) O(n^3) O(n3)
AC代码:

#include 
using namespace std;
typedef long long int ll;
const int N = 1e5+5;
const double eps = 1e-5;
int n;
int a[105][105];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            cin>>a[i][j];
            a[i][j]+=a[i-1][j];
        }
    int res=-1e9;
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++){
            int mp=0;
            for(int k=1;k<=n;k++){
                int tp=a[j][k]-a[i-1][k];
                if(mp>=0) mp+=tp;
                else mp=tp;
                res=max(res,mp);
            }
        }
    }
    cout<<res;
    return 0;
}
问题 G: Task

题目描述
Today the company has m tasks to complete. The ith task need xi minutes to complete. Meanwhile, this task has a difficulty level yi. The machine whose level below this task’s level yi cannot complete this task. If the company completes this task, they will get (500xi+2yi) dollars.
The company has n machines. Each machine has a maximum working time and a level. If the time for the task is more than the maximum working time of the machine, the machine can not complete this task. Each machine can only complete a task one day. Each task can only be completed by one machine.
The company hopes to maximize the number of the tasks which they can complete today. If there are multiple solutions, they hopes to make the money maximum.

输入
The input contains several test cases.
The first line contains two integers N and M. N is the number of the machines.M is the number of tasks(1 < =N <= 100000,1<=M<=100000).
The following N lines each contains two integers xi(0 The following M lines each contains two integers xi(0

输出
For each test case, output two integers, the maximum number of the tasks which the company can complete today and the money they will get.

样例输入

1 2
100 3
100 2
100 1

样例输出

1 50004

题目大意:有n机器和m个任务,每台机器有最长运行时间和级别,每个任务有运行时间和级别只有当某任务运行时间<=某机器最长运行时间并且机器级别高于任务的级别该任务才能分配到这台机器上运行,能获得收益:500x+2y(x表示时间,y表示级别),要求求出如何安排能获得最大收益。
思路:本质上是机器、任务的二分图,求出一个最大匹配。本题中每个匹配收益为500x+2y,且0 AC代码:

#include 
using namespace std;
typedef long long int ll;
const int N = 1e5+5;
const double eps = 1e-5;
struct nd
{
    int x,y;
    bool operator<(nd t){
        if(x==t.x) return y>t.y;
        return x>t.x;
    }
}task[N],ma[N];
int main()
{
    int n,m;
    multiset<int>rs;
    ll res,cnt;
    while (cin>>n>>m)
    {
        rs.clear();
        res=0,cnt=0;
        for(int i=0;i<n;i++) cin>>ma[i].x>>ma[i].y;
        for(int i=0;i<m;i++) cin>>task[i].x>>task[i].y;
        sort(ma,ma+n);
        sort(task,task+m);
        int j=0;
        for(int i=0;i<m;i++){
            while (j<n&&ma[j].x>=task[i].x) rs.insert(ma[j++].y);
            auto t=rs.lower_bound(task[i].y);
            if(t!=rs.end()){
                cnt++;
                res+=500*task[i].x+task[i].y*2;
                rs.erase(t);
            }
        }
        cout<<cnt<<' '<<res<<'\n';
    }
    return 0;
}
问题 H: Largest Rectangle in a Histogram

题目描述
A histogram is a polygon composed of a sequence of rectangles aligned at a common base line. The rectangles have equal widths but may have different heights. For example, the figure on the left shows the histogram that consists of rectangles with the heights 2, 1, 4, 5, 1, 3, 3, measured in units where 1 is the width of the rectangles:
蓝桥杯国赛五一训练赛(1)_第1张图片

Usually, histograms are used to represent discrete distributions, e.g., the frequencies of characters in texts. Note that the order of the rectangles, i.e., their heights, is important. Calculate the area of the largest rectangle in a histogram that is aligned at the common base line, too. The figure on the right shows the largest aligned rectangle for the depicted histogram.
输入
The input contains several test cases. Each test case describes a histogram and starts with an integer n, denoting the number of rectangles it is composed of. You may assume that 1≤n≤100000. Then follow n integers h1…hn, where 0≤hi≤10000000000. These numbers denote the heights of the rectangles of the histogram in left-to-right order. The width of each rectangle is 1. A zero follows the input for the last test case.
输出
For each test case output on a single line the area of the largest rectangle in the specified histogram. Remember that this rectangle must be aligned at the common base line.
样例输入

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

样例输出

8
4000

提示
Huge input, scanf is recommended.
题目大意:给一系列高不同的矩形,宽都为1,在这些矩形里找到最大的面积,例如题目描述中的阴影部分为最大值,单调栈经典题,维护一个高度递增的栈,在遍历过程中找到最大面积
AC代码:

#include 
using namespace std;
typedef long long int ll;
const int N = 1e5+5;
const double eps = 1e-5;
int main()
{
    int n;
    vector<pair<ll,ll>>stk;
    ll x,h,res;
    pair<ll,ll>tp;
    while (cin>>n,n)
    {
        res=0;
        stk.clear();
        for(int i=0;i<n;i++){
            scanf("%lld",&x);
            if(stk.empty()) stk.push_back({x,1});
            else{
                h=0;
                while (stk.size())
                {
                    tp=stk.back();
                    if(tp.first<=x) break;
                    stk.pop_back();
                    h+=tp.second;
                    res=max(res,h*tp.first);
                }
                stk.push_back({x,h+1});      
            }
        }
        h=0;
        while (stk.size())
        {
            tp=stk.back();
            stk.pop_back();
            h+=tp.second;
            res=max(res,h*tp.first);
        }
        printf("%lld\n",res);
    }
     
    return 0;
}
问题 I: 邻值查找

题目描述
给定一个长度为 n 的序列 A,A 中的数各不相同。对于 A 中的每一个数Ai,求:min(1≤j 以及令上式取到最小值的 j(记为Pi)。若最小值点不唯一,则选择使Aj较小的那个。
输入
第一行一个整数n,第二行n个数A1…An。
输出
n-1行,每行2个用空格隔开的整数。分别表示当i取2~n时,对应的min(1≤j 样例输入

3
1 5 3

样例输出

4 1
2 1

提示
对于30%的数据:n≤100
对于70%的数据:n≤104
对于100%的数据:n≤10
5
思路:可以有离线和在线的做法,离线做法即排序后利用双向链快速找到每个数的前驱和后继再从两者选出最优,或是利用STL中的Set动态维护一个有序的序列,并且Set也可以很方便得出一个数的前驱和后继
AC代码
法一、链表离线:

#include 
using namespace std;
typedef long long int ll;
const int N = 1e5+5;
const double eps = 1e-5;
pair<ll,int>a[N],res[N];
int l[N],r[N],p[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i].first;
        a[i].second=i;
    }
    sort(a+1,a+n+1);
    a[0].first=-1e10;
    a[n+1].first=1e10;
    for(int i=1;i<=n;i++){
        l[i]=i-1;r[i]=i+1;
        p[a[i].second]=i;
    }
    for(int i=n;i>1;i--){
        int pos=p[i];
        int lf=l[pos],rt=r[pos];
        ll lv=a[pos].first-a[lf].first;
        ll rv=a[rt].first-a[pos].first;
        if(lv>rv) res[i]={rv,a[rt].second};
        else res[i]={lv,a[lf].second};
        l[rt]=lf;r[lf]=rt;
    }
    for(int i=2;i<=n;i++) printf("%lld %d\n",res[i].first,res[i].second);
    return 0;
}

法二、Set在线处理:

#include 
using namespace std;
typedef long long int ll;
const int N = 1e5+5;
const double eps = 1e-5;
int main()
{
    int n;
    scanf("%d",&n);
    ll a;
    set<pair<ll,int>>res;
    res.insert({-1e10,-1});
    res.insert({1e10,1e5});
    for(int i=1;i<=n;i++){
        scanf("%lld",&a);
        auto t=res.insert({a,i}).first;//insert()会返回插入后元素的迭代器
        if(i!=1){
            auto l=t,r=t;
            l--;r++;
            ll lv,rv;
            lv=t->first-l->first;rv=r->first-t->first;
            if(rv<lv) printf("%lld %d\n",rv,r->second);
            else printf("%lld %d\n",lv,l->second);
        }
    }
    return 0;
}

你可能感兴趣的:(菜鸡成长之路,蓝桥杯,c++,算法)