牛客小白月赛4 (都是好题)

A

链接:https://www.nowcoder.com/acm/contest/134/A

来源:牛客网

题目描述

铁子从森林里收集了n根木棍,她开始将它们按顺序的排成一排,从左到右依次为1到n,她回想起
在数学课上老师教她的三角形知识,她开始从这些木棍中间找三根木棍来组成一个周长最大的三角形,
这时她的兄弟顺溜偷偷的溜了过来,偷走了第i根木棍,现在她想知道现在能够组成周长最大的三角形
的周长是多少?

输入描述:

 
   
第一行两个整数n和q。(1 ≤ n, q ≤ 10 5) 第二行n个整数表示第i根木棍的长度a i。(1 ≤ a i ≤ 10 9) 接下来q行,每行一个整数表示被顺溜偷走的木棍编号。注意每行的事件是独立的,也就是说每一次操作都是对于原来的n根木棍进行的。

输出描述:

对于每个询问输出一行表示答案,如果删除木棍后无法组成三角形则输出 -1 。


思路:

按照三角形最长边小于其余两边之和来从长到短选择最长边。


#include 
 
using namespace std;
 
typedef long long ll;
typedef unsigned long long ull;
 
typedef vector vi;
typedef vector vii;
typedef vector vll;
 
const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;
 
int n, m, k;
 
typedef struct Node{
    int v, id;
    bool operator < (const Node &rhs) const{
        return v < rhs.v;
    }
}Node;
 
vector p;
 
int main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << setprecision(10) << fixed;
    cin >> n >> k;
    p.resize(n);
    for(int i = 0; i < n; i++){
        cin >> p[i].v;
        p[i].id = i;
    }
    sort(p.begin(), p.end());
    while(k--){
        int ban;
        cin >> ban;
        ban--;
        ll res = -1;
        for(int i = n - 1; i >= 2; i--){
            if(p[i].id == ban)
                continue;
            int sum = 0;
            for(int j = i - 1, cnt = 0; j >= 0 && cnt < 2; j--)
                if(p[j].id == ban)
                    continue;
                else{
                    cnt++;
                    sum += p[j].v;
                }
            if(sum > p[i].v){
                res = (ll)sum + (ll)p[i].v;
                break;
            }
        }
        cout << res << endl;
    }
    cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
    return 0;
}


B

链接:https://www.nowcoder.com/acm/contest/134/B
来源:牛客网

题目描述

铁子和顺溜在学习了博弈论的sg函数之后,解决了很多很多博弈题,现在他们遇到了一道难题。
给出一个长度为 n 的数列,数列里的每个元素都是个位数,这个数列的每一个连续子数列都能生成
一个十进制数,对于子数列a[l~r],这个十进制数的个位为a[r],十位为a[r - 1],...,最高位
为a[l]。
现在铁子需要知道最小的不能被该数列的子数列生成的十进制非负整数是多少?

输入描述:

第一行一个数字n。(1 ≤ n ≤ 1000)
第二行n个数字di。(0 ≤ di ≤ 9)

输出描述:

输出一个数字表示答案。


思路:

从小到大猜。


#include 
 
using namespace std;
 
typedef long long ll;
typedef unsigned long long ull;
 
typedef vector vi;
typedef vector vii;
typedef vector vll;
 
const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;
 
int n, m, k;
 
vi p;
 
int main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << setprecision(10) << fixed;
    cin >> n;
    p.resize(n);
    for(int i = 0; i < n; i++)
        cin >> p[i];
    bool zero = false;
    for(int i = 0; i < n; i++)
        if(p[i] == zero){
            zero = true;
            break;
        }
    if(!zero)
        cout << 0 << endl;
    else{
        for(int i = 0; ; i++){
            bool found = false;
            for(int j = n - 1; j >= 0; j--){
                int clone = i, t = j;
            while(t >= 0 && clone && clone % 10 == p[t]){
                t--;
                clone /= 10;
            }
            if(!clone){
                found = true;
                break;
            }
        }
        if(!found){
            cout << i << endl;
            break;
        }
    }
    }
    cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
    return 0;
}


C

链接:https://www.nowcoder.com/acm/contest/134/C
来源:牛客网


题目描述

铁子和顺溜上生物课的时候不小心将几滴超级病菌滴到了培养皿上,这可急坏了他们。
培养皿可以被看成一个n*n的方格,最初病菌滴在了这n*n的格子中的某些格子,病菌的传染方式是这样的,如果一个方格与两个或多个被感染的方格相邻(两个方格相邻当且仅当它们只有一条公共边),
那么它就会被感染。现在铁子和顺溜想知道,最终所有的方格会不会都被感染。

输入描述:

第一行两个整数n,m。n表示方格的规格,m表示最初病菌所在的格子数。(1 ≤ n ≤ 1000, 0 < m < n)。
接下来m行每行两个整数x i,y i表示第x i行的第y i个格子有病菌。
数据保证不会有两个病菌初始时在同一个格子。

输出描述:

如果最终所有的方格都会被感染,输出 YES。
否则输出 NO。

思路:

可以直观发现,只有当m >= n时才会铺满整个培养皿。

因为只有有两个相邻的感染才能被感染,也就是说如果要让(i,j)感染,i行或者i + 1行必须要有感染的才有可能。

如果m < n,显然无法使得所有行都感染。

如果m == n,放对角线就可以了。


#include 

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector vi;
typedef vector vii;
typedef vector vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	cin >> n >> m;
	if(m >= n)
		cout << "YES" << endl;
	else
		cout << "NO" << endl;
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


D

链接:https://www.nowcoder.com/acm/contest/134/D
来源:牛客网

题目描述

今天春天铁子的班上组织了一场春游,在铁子的城市里有n个郊区和m条无向道路,第i条道路连接郊区A i和B i,路费是C i。经过铁子和顺溜的提议,他们决定去其中的R个郊区玩耍(不考虑玩耍的顺序),但是由于他们的班费紧张,所以需要找到一条旅游路线使得他们的花费最少,假设他们制定的旅游路线为V 1, V ,V ... V R,那么他们的总花费为从V 1到V 2的花费加上V 2到V 3的花费依次类推,注意从铁子班上到V 1的花费和从V R到铁子班上的花费是不需要考虑的,因为这两段花费由学校报销而且我们也不打算告诉你铁子学校的位置。

输入描述:

第一行三个整数n, m, R(2 ≤ n ≤ 200, 1 ≤ m ≤ 5000, 2 ≤ R ≤ min(n, 15))。
第二行R个整数表示需要去玩耍的郊区编号。
以下m行每行Ai, Bi, Ci(1 ≤ Ai, B≤ n, A≠ Bi, C≤ 10000)
保证不存在重边。

输出描述:

输出一行表示最小的花费

思路:

floyd预处理,然后用dp记录结果枚举。

dp[i][j],i代表此时的状态,j代表最后一次选的点。


#include 

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector vi;
typedef vector vii;
typedef vector vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

vi p;
vii path, dp;

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	cin >> n >> m >> k;
	p.resize(k);
	for(int i = 0; i < k; i++){
		cin >> p[i];
		p[i]--;
	}
	path.resize(n, vi(n, INF));
	for(int i = 0; i < m; i++){
		int u, v, c;
		cin >> u >> v >> c;
		u--;
		v--;
		path[u][v] = c;
		path[v][u] = c;
	}
	for(int i = 0; i < n; i++)
		path[i][i] = 0;
	for(int t = 0; t < n; t++)
		for(int i = 0; i < n; i++)
			for(int j = 0; j < n; j++)
				path[i][j] = min(path[i][j], path[i][t] + path[t][j]);
	dp.resize(1 << k, vi(k, INF));
	for(int i = 0; i < k; i++)
		dp[1 << i][i] = 0;
	for(int i = 0; i < (1 << k); i++){
		for(int j = 0; j < k; j++){
			if(!(i & (1 << j)))
				continue;
			for(int t = 0; t < k; t++){
				if((1 << t) & i)
					continue;
				dp[i | (1 << t)][t] = min(dp[i | (1 << t)][t], dp[i][j] + path[p[j]][p[t]]);
			}
		}
	}
	int res = INF;
	for(int i = 0; i < k; i++)
		res = min(res, dp[(1 << k) - 1][i]);
	cout << res << endl;
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


E

链接:https://www.nowcoder.com/acm/contest/134/E
来源:牛客网

题目描述

给你一个浮点数,将它原样输出。

输入描述:

输入只有一行,一个浮点数。保证数据合法,不存在整数位的前导零以及小数位的后导零。

输出描述:

输出只有一行,一个浮点数。

思路:

用来启发人们发现计算机的浮点数是有误差的。

把输入当字符串处理即可。


#include 

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector vi;
typedef vector vii;
typedef vector vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

string str;

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	cin >> str;
	cout << str << endl;
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


F

链接:https://www.nowcoder.com/acm/contest/134/F
来源:牛客网

题目描述

一串长度为 n 的字符串 A 和一串长度为 m 的字符串 B。并且这两串字符串只会含有 0 或 1 。

铁子可以对字符串 A 执行两种操作,两种操作可以执行任意次。

操作1(无情替换):铁子可以用 11 替换掉 0 ,也可以用 00 替换掉 1 .

操作2(极限删除):铁子可以删除掉 111 ,也可以删除 000 .

现在问,字符串 A 可以变成字符串 B 吗?

输入描述:

第一行有一个整数T,表示有T(1<=T<=1000)组测试数据。

接下来的每组数据,第一行有两个整数n,m(1<=n,m<=100),表示字符串A和字符串B的长度。

接下来有两行字符串,分别表示字符串A和字符串B。

输出描述:

对于每组测试数据,如果字符串A可以变为字符串B,则输出一行”YES”,否则输出一行”NO”.输出不包括引号。

思路:

可以发现顺序是无关的,也就是说001一定可以从100或者010转换而来。

又可以发现,1可以自增成1111(4个1)或者1111111(7个1),也就是说可以自增3n的长度,当然n属于R。

那么现在需要做的就是把A和B都转化成0或者1的串,然后判断他们长度之间的关系。


#include 

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector vi;
typedef vector vii;
typedef vector vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

string str, ptr;

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	int times;
	cin >> times;
	while(times--){
		cin >> n >> m;
		cin >> str >> ptr;
		int res = 0;
		for(int i = 0; i < n; i++)
			res += 1 + str[i] - '0';
		for(int i = 0; i < m; i++)
			res -= 1 + ptr[i] - '0';
		if(res % 3)
			cout << "NO" << endl;
		else 
			cout << "YES" << endl;
	}
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


G

链接:https://www.nowcoder.com/acm/contest/134/G
来源:牛客网

题目描述

端午节前夕,铁子和顺溜二人聊到了黑白棋,黑白棋是一个历史悠久的益智游戏,但是双方都觉得自己比较厉害谁也不服谁,然后开始了黑白棋探讨之旅,但是谁也没想到在一边观战的城哥拥有着一个预知未来的能力,能准确预知未来下子的位置(x,y),由于能力有限,却不能得知每个回合落子的颜色,但是能根据落子的位置判断是黑方还是白方,现在城哥想知道最后黑白双方的棋子比例是多少。(黑方先手)

------分割线-------------------------------------------------------------

下面是黑白棋规则介绍:

这张图片是黑白棋的标准开局图,每局由执黑色棋玩家先下子,交互下子。直到游戏结束!
牛客小白月赛4 (都是好题)_第1张图片
棋盘为8×8的方格布局,开局时在棋盘正中有摆好的四枚棋子,黑白各2枚,交叉放置,由执黑棋的一方先落子,双方交替下子,棋子落在方格内,一局游戏结束后双方更换执子颜色。

合法的棋步包括:在一个空格新落下一个棋子,并且翻转对手一个或多个棋子。
下子方式:把自己颜色的棋子放在棋盘的空格上,而当自己放下的棋子在横、竖、斜八个方向内有一个自己的棋子,则被夹在中间的对方棋子全部翻转会成为自己的棋子。夹住的位置上必须全部是对手的棋子,不能有空格。并且,只有在可以翻转棋子的地方才可以下子。
一步棋可以在数个方向上翻棋,任何被夹住的棋子都必须被翻转过来,棋手无权选择不去翻某个棋子必须是刚下的子夹对方才能够翻对方的子,因翻转对方的棋子而夹住的子是不能被翻的。
翻转棋子时,每次下子最少必须翻转对方一个棋子,若棋局中下子都不能翻转对方棋子,则自动pass轮空,己方无子可下,由对方继续下子。两个玩家都不能下子翻转对方棋子,游戏结束。

输入描述:

每行两个数字x,y代表落子坐标(1 ≤ x,y ≤ 8)

以EOF结束下子,保证下子的正确性以及合法性,下完最后一颗子代表棋局结束。

ps:emmmm... 貌似有点长,不要慌,问题不大(雾

输出描述:

输出一行,黑方与白方的棋子数量之比a:b

思路:

有研究黑白棋的朋友吗?

从这题中我感受到了黑白棋的魅力,首先如果棋盘是n*m,那么最多放n*m - 4个棋,而且只要知道放的棋的位置,甚至不用知道先后顺序和颜色,就一定可以复原成唯一的棋盘。


#include 

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector vi;
typedef vector vii;
typedef vector vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;
const int dr[] = {-1, -1, -1, 0, 0, 1, 1, 1};
const int dc[] = {-1, 0, 1, -1, 1, -1, 0, 1};

int n, m, k;

vi x, y, p;
vii mat, inq;

void recur(int depth, int id){
	int u, v;
	if(depth > (int)x.size())
		return;
	bool found = false;
	for(int i = 0; i < (int)x.size(); i++){
		u = x[i];
		v = y[i];
		if(mat[u][v] >= 0)
			continue;
		for(int dir = 0; dir < 8; dir++){
			int cnt = 0;
			int uu = u + dr[dir];
			int vv = v + dc[dir];
			if(!(uu > 0 && vv > 0 && uu <= 8 && vv <= 8))
				continue;
			if(mat[uu][vv] == id)
				continue;
			while(cnt < 3 && uu > 0 && vv > 0 && uu <= 8 && vv <= 8 && mat[uu][vv] >= 0){
				cnt |= (1 << mat[uu][vv]);
				if(cnt == 3)
					break;
				uu += dr[dir];
				vv += dc[dir];
			}
			if(cnt == 3){
				found = true;
				while(u != uu || v != vv){
					mat[u][v] = mat[uu][vv];
					u += dr[dir];
					v += dc[dir];
				}
				u = x[i];
				v = y[i];
			}
		}
		if(found)
			return recur(depth + 1, id ^ 1);
	}
	return recur(depth, id ^ 1);
}

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	mat.resize(10, vi(10, -1));
	inq.resize(100, vi(5));
	mat[4][4] = mat[5][5] = 0;
	mat[5][4] = mat[4][5] = 1;
	int u, v;
	while(cin >> u >> v){
		x.push_back(u);
		y.push_back(v);
	}
	recur(1, 1);
	p.resize(2);
	for(int i = 1; i <= 8; i++)
		for(int j = 1; j <= 8; j++){
			if(mat[i][j] < 0)
				continue;
			p[mat[i][j]]++;
		}
	cout << p[1] << ":" << p[0] << endl;
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


H

链接:https://www.nowcoder.com/acm/contest/134/H
来源:牛客网

题目描述

有n个盒子摆成一排,每个盒子内都有a i个糖果。
现在你可以执行以下操作:
·你可以选择任意一个盒子,在选择的盒子内吃掉一个糖果。
对你的要求如下:
·任何m个相邻的盒子内糖果数量不能超过x个。
请问,实现要求的最少操作次数是多少?

输入描述:

第一行三个数字n, m, x(2 ≤ n,m ≤ 106,1 ≤ x ≤ 109)。
第二行n个数字(1 ≤ ai ≤ 109)。

输出描述:

输出一个操作数,代表实现要求的最少操作数。


思路:

利用滑动窗口的想法,从左到右扫描,优先吃掉右边的。

不必害怕是否会退化成(nm), 可以证明,当此时扫到i时,i - m退出,如果此时val[i] > val[i - m]那才有可能要吃,而且只用吃一个。

实际复杂度为O(n)。


#include 

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector vi;
typedef vector vii;
typedef vector vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

vll p;

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	cin >> n >> m >> k;
	p.resize(n + 1);
	for(int i = 1; i <= n; i++)
		cin >> p[i];
	ll cur = 0, res = 0;
	for(int i = 1; i <= min(n, m - 1); i++)
		cur += p[i];
	for(int i = min(n, m); i <= n; i++){
		cur += p[i];
		for(int j = i; cur > k; j--){
			ll cal = min(cur - k, p[j]);
			res += cal;
			p[j] -= cal;
			cur -= cal;
		}
		cur -= p[i - m + 1];
	}
	cout << res << endl;
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


H

链接:https://www.nowcoder.com/acm/contest/134/I
来源:牛客网

题目描述

铁子的班级在毕业晚会有一个合唱节目,到了毕业晚会的时候,他们必须排成一排一起合唱"认错","当然是选择原谅他"等一系列原谅歌曲,但是当队形布置好的时候,领导们觉得队形里最长的连续的女生的长度太小了,但是由于马上要开始演唱,所以最多只能两个人交换位置,问新队形中最长的连续的女生的长度是多少?

输入描述:

第一行一个数字n表示铁子班级的总人数。1≤n≤105
第二行一个字符串s表示最初的队形布置,si=0表示第i个人是女生,si=1表示第i个人是男生。

输出描述:

输出一行表示答案


思路:

思考这样一种情形,可以改变一个人的性格,问最长的连续女生是多少?

这个问题可以转换成上述情形,最后只用输出min(女生总人数,上述情形计算结果)即可;


#include 

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector vi;
typedef vector vii;
typedef vector vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

string str;
vii dp;

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	cin >> n;
	dp.resize(n, vi(2));
	cin >> str;
	cerr << str << endl;
	int res = 0, cnt = 0;
	cnt = dp[0][0] = !(str[0] - '0');
	dp[0][1] = 1;
	for(int i = 1; i < n; i++){
		if(!(str[i] - '0')){
			cnt++;
			dp[i][0] = dp[i - 1][0] + 1;
			dp[i][1] = dp[i - 1][1] + 1;
		}
		else{
			dp[i][0] = 0;
			dp[i][1] = dp[i - 1][0] + 1;	
		}
		res = max(res, max(dp[i][0], dp[i][1]));
	}
	cout << min(res, cnt) << endl;
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


J

链接:https://www.nowcoder.com/acm/contest/134/J
来源:牛客网

题目描述

铁子最近犯上了强迫症,他总是想要把一个序列里的元素变得两两不同,而他每次可以执行一个这样的操作,他可以选择序列里的任意两个元素相加,不妨记作a i和a j,然后把a i+a j放进序列里,再删掉a i和a j其中的随便一个,问最少操作多少次可以完成铁子的愿望?

输入描述:

第一行一个整数n表示序列的长度(1≤n≤105)
第二行n个整数ai表示序列的每个整数(1≤ai≤109)

输出描述:

输出一行表示答案

思路:

首先先要理解题意,发现可以除去一个元素,但要加入一个新元素,为了不使新元素有任何影响,让新元素比任何元素大即可。

这样就可以排除新元素的影响, 题意也就可以变成要除去多少个元素使得序列无重复元素。


#include 

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef vector vi;
typedef vector vii;
typedef vector vll;

const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;

int n, m, k;

vi p;

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << setprecision(10) << fixed;
	cin >> n;
	p.resize(n);
	for(int i = 0; i < n; i++)
		cin >> p[i];
	sort(p.begin(), p.end());
	cout << n - (unique(p.begin(), p.end()) - p.begin()) << endl;
	cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
	return 0;
}


未来可期。

你可能感兴趣的:(牛客网习题)