给定一个字符串,你可以删除多个(可以是 0 0 0) 相同 的字符,这样操作之后,你能否得到一个回文串?如果能,求最小化删除的个数。
多组数据。
每一组数据包含两行,分别为字符串的长度 N N N,以及一个仅由小写字母组成的字符串 S S S。
对于每一组数据,输出一行。
如果不可能得到一个回文串,输出 − 1 −1 −1。反之则输出最小操作次数。
4
8
bilibili
3
qwq
9
daimayuan
7
xcpcxpc
1
0
-1
2
解释:
在第一个例子中,删除开头的 b
得到 ilibili
。
第二个例子中,qwq
本身已回文,不需要操作。
第三个例子中,可以看到 daimayuan
不能靠仅删除一种字符得到一个回文串。
1 ≤ N ≤ 1 0 5 1≤N≤10^5 1≤N≤105, 但保证 ∑ N ≤ 2 × 1 0 5 \sum{N}≤2×10^5 ∑N≤2×105
并没有巧妙的办法,就是简单的枚举 26 26 26种字母,找出其中删除的最少的即可
检测的思路如下:
从左右两端开始匹配
(1)if str[left] == str[right]
,left++, right--
(2)if str[left] != str[right]
如果左侧字符可以删除,那么删除左侧字符(left++
)
如果右侧字符可以删除,那么删除右侧字符(right--
)
如果左右都不可以删除,本轮删除失败,继续尝试下一个字符
#include
#include
using namespace std;
const int max_n = 2e5;
const string compose = "abcdefghijklmnopqrstuvwxyz";
string str;
int n1, n2;
int main() {
cin >> n1;
for (int i = 0; i < n1; i++) {
cin >> n2 >> str;
int ans = -1;
for (char c: compose) {
int l = 0, r = n2 - 1;
int temp = 0;
while (l <= r) {
if (str[l] == str[r]) {
l++; r--;
}
else {
if (str[l] == c) {
l++;
temp++;
}
else if (str[r] == c) {
r--;
temp++;
}
else {
temp = -1;
break;
}
}
}
if (temp != -1)
if (ans == -1) ans = temp;
else ans = min(ans, temp);
}
cout << ans << endl;
}
return 0;
}
给定一个长度为 n n n 的数组 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an,接下来进行 n − 1 n−1 n−1 次操作。每次选择一个下标 x x x ,将 a x a_x ax 和 a x + 1 a_{x+1} ax+1 合并成 a x ∗ a x + 1 m o d 1000003 a_x*a_{x+1}mod1000003 ax∗ax+1mod1000003 ,并且你会获得 ( a x − a x + 1 ) 2 (a_x−a_{x+1})^2 (ax−ax+1)2 的分数。
所以每次操作后,数组的长度将会减 1 1 1,当最后只剩下一个元素时停止操作。输出最终能获得的最大分数。
第一行一个数字 n n n。
接下来一行 n n n 个整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an。
一个数,表示答案。
3
1 2 3
26
所有数据保证 1 ≤ n ≤ 300 , 1 ≤ a i ≤ 1 0 6 1≤n≤300,1≤a_i≤10^6 1≤n≤300,1≤ai≤106。
是没见过的船新版本DP
首先简单说明一下每轮操作的含义
对于要合并的长度为 l e n len len的区间 [ l , r ] [l,r] [l,r],我们先分别合并左右两部分 [ l , k ] [l,k] [l,k]和 [ k + 1 , r ] [k+1,r] [k+1,r],那么容易知道:
dp[l][r] = max(dp[l][r], dp[l][k] + dp[k + 1][r] + (num[l][k] - num[k + 1][r]) ** 2)
也就是说合并区间 [ l , r ] [l,r] [l,r]所得到的分数等于分别合并左右区间得到的分数再加上最后一次合并得到的分数
D P DP DP的基本思想就是对于任意一个长度为 l e n len len的区间(第一重循环),我们尝试其所有可能的划分(第三重循环),找出其中的最大值
第二重循环?它负责遍历每一个长度为 l e n len len的区间
而我们从小到大遍历 l e n len len,保证了在动态规划过程中,所有长度小于 l e n len len的区间最优结构已经得到,所以 D P DP DP是可行的
AC代码如下
#include
using namespace std;
const int mod_num = 1000003;
const int max_n = 300;
long long num[max_n + 1][max_n + 1], dp[max_n + 1][max_n + 1];
int n;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> num[i][i];
for (int i = 1; i < n; i++) {
for (int j = i + 1; j <= n; j++) {
num[i][j] = num[j][j];
num[i][j] = (num[i][j] * num[i][j - 1]) % mod_num;
}
}
for (int len = 2; len <= n; len++) {
for (int l = 1, r = len; r <= n; l++, r++) {
for (int k = l; k < r; k++) {
dp[l][r] = max(dp[l][r], dp[l][k] + dp[k + 1][r] + (num[l][k] - num[k + 1][r]) * (num[l][k] - num[k + 1][r]));
}
}
}
cout << dp[1][n] << endl;
return 0;
}
接着《饿饿 饭饭》 的故事,在两天后,食堂的工作人员回来了,整个食堂又回到了原来井井有条的状态。
两个月后,由于天气越来越热,大家的胃口越来越小了,作为食堂管理员的 C C CC CC非常担心孩子们的身体健康,所以他决定开展一个活动来调动孩子们吃饭的积极性,顺便考验一下孩子们的数学水平。活动内容如下:
先让每一个孩子都抽一个球,每一个球上有一个数字, 然后给这个孩子 n n n个数字,每一个孩子都有无数次操作机会,每一次都会选中一个数将它乘上 2 2 2,或者乘上 3 3 3,请问这个孩子可以通过上面的操作将这 n n n个数都变成相同的吗?
如果回答正确,这个回答正确的孩子就可以得到一份免费的午餐,但是这对于孩子们来说是在是太困难了,但是他们都想吃到免费的午餐,所以他们都想请你告诉他们正确的答案,让他们都迟到免费的午餐。
第 1 1 1行给定一个数 T T T,表示有 T T T个小孩子请你告诉他正确的答案。
第 2 2 2到 T + 1 T+1 T+1行,第 1 1 1个数是每个孩子抽到的数字 n n n,第 2 2 2到 n + 1 n+1 n+1个数是对应的 n n n个数字。
如果可以变成相同的,输出YES
。如果不能变成相同的,输出NO
。
1 ≤ T ≤ 100 , 1 ≤ n ≤ 2 × 1 0 5 , 1 ≤ a i ≤ 1 0 9 1≤T≤100,1≤n≤2×10^5,1≤a_i≤10^9 1≤T≤100,1≤n≤2×105,1≤ai≤109
数据保证 ∑ i = 1 T n ≤ 2 × 1 0 5 \sum^{T}_{i=1}{n}≤2×10^5 ∑i=1Tn≤2×105
2
4 75 150 75 50
3 100 150 250
YES
NO
将每个数字拆解成两部分的乘积: 2 、 3 2、3 2、3和其他数字
我们尝试把所有数字的第一部分除去,如果剩余部分相等,输出YES
反之,输出NO
AC代码如下
#include
using namespace std;
const int max_t = 100;
const int max_n = 2e5;
const int max_a = 1e9;
int t, n;
int num_arr[max_n];
int main() {
cin >> t;
for (int i = 0; i < t; i++) {
cin >> n;
for (int j = 0; j < n; j++) {
cin >> num_arr[j];
while (num_arr[j] % 2 == 0) num_arr[j] /= 2;
while (num_arr[j] % 3 == 0) num_arr[j] /= 3;
}
int j, same = num_arr[0];
for (j = 1; j < n; j++) if (num_arr[j] != same) break;
if (j != n) cout << "NO" << endl;
else cout << "YES" << endl;
}
return 0;
}
对于一个字符串 S S S ,我们定义 f ( S ) f(S) f(S) 为 S S S 中出现的不同的字符个数。 例如 f ( a b a ) = 2 f(aba)=2 f(aba)=2, f ( a b c ) = 3 f(abc)=3 f(abc)=3, f ( a a a ) = 1 f(aaa)=1 f(aaa)=1。
现在给定一个字符串 S S S (假设长度为 l e n len len),请你计算 ∑ i = 0 l e n − 1 ∑ j = i l e n − 1 f ( S [ i : j ] ) \sum_{i=0}^{len−1}\sum_{j=i}^{len−1}f(S[i:j]) ∑i=0len−1∑j=ilen−1f(S[i:j]) 。
输入一行包含一个由小写字母组成的字符串 S S S 。
输出一个整数表示答案。
ababc
28
所有数据保证字符串长度 l e n ≤ 1000000 len≤1000000 len≤1000000,字符串下标从 0 0 0 到 l e n − 1 len−1 len−1。
直接正面求解每一个字符串的分值显然会 T L E TLE TLE,那么尝试其他方法
我们的想法是单独处理每一个字符的分值,然后累加起来
直接去找该字符出现在多少字符串中并不容易,正难则反,我们尝试去寻找该字符没有出现在多少字符串中
举个例子ababc
,其中a
没有出现的字符串显然只有b
,b
,c
,bc
所以想要找到所有没有出现a
的字符串,我们只需要保存a
出现过的每一个位置,然后根据间隔字符串长度计算即可
AC代码如下
#include
#include
using namespace std;
const string alpha = "abcdefghijklmnopqrstuvwxyz";
const int max_len = 1e6;
vector<int>vocab[26];
long long ans = 0;
inline long long calc(long long num) {
return num * (num + 1) / 2;
}
int main() {
string str;
cin >> str;
int len = str.size();
for (int i = 0; i < len; i++) {
vocab[str[i] - 'a'].push_back(i);
}
for (char c : alpha) {
long long sum = calc(len);
if (!vocab[c - 'a'].empty()) {
int l = 0;
auto iter = vocab[c - 'a'].begin();
while (iter != vocab[c - 'a'].end()) {
sum -= calc(*iter - l);
l = *iter + 1; iter++;
}
sum -= calc(len - l);
ans += sum;
}
}
cout << ans << endl;
return 0;
}
后排提醒:要开long long
,否则会炸
便利蜂的货架上摆了一排蒟蒻果冻,搞得鶸尛鱻眼花缭乱…
对于每个果冻,都有一个价格 w w w 和口感 t t t。鶸尛鱻有一个购物篮子,在挑选蒟蒻果冻的时候,他有以下几种操作:
鶸尛鱻不喜欢重复,当操作 1 1 1 的 价格或口感 与篮中已有果冻重复时,他会立刻将其放回货架。
经过 n n n 次操作后,鶸尛鱻确定了要购买的若干果冻,请你帮他求出篮子里果冻的总价格。
第 1 1 1 行一个正整数 n n n,代表操作次数。
第 2 2 2 行至第 ( n + 1 n+1 n+1)( n + 1 n+1 n+1) 行,每行 一个或三个 整数,分别表示 o p op op, w w w, t t t。
w w w 和 t t t 当且仅当 o p = 1 op=1 op=1 时存在。
输出一个整数,表示篮子里果冻的总价格。
6
1 1 1
1 2 5
2
1 3 3
3
1 5 2
7
所有数据保证 1 ≤ n ≤ 1 0 5 1≤n≤10^5 1≤n≤105, 1 ≤ w , t ≤ 1 0 6 1≤w,t≤10^6 1≤w,t≤106,且保证输入合法。
需要维护两个顺序,一个是价格的升序排序,一个是口感的升序排序
显然需要维护两个数组,关键在于使两个数组维持同步
利用map
的erase
操作可以根据键值删除元素的性质,可以很容易实现这一点
AC代码如下
#include
#include
using namespace std;
const int max_n = 1e5;
const int max_w = 1e6;
const int max_t = 1e6;
map<int, int>m1, m2;
int w, t, n, op;
long long ans;
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> op;
if (op == 1) {
cin >> w >> t;
if (m1.count(w) == 0 && m2.count(t) == 0) {
m1[w] = t;
m2[t] = w;
}
}
else if (op == 2) {
m2.erase(m1.begin()->second);
m1.erase(m1.begin());
}
else {
m1.erase(m2.begin()->second);
m2.erase(m2.begin());
}
}
for (auto it : m1) {
ans += (long long)(it.first);
}
cout << ans << endl;
return 0;
}
有 n n n个玩家参加比赛,他们分别有能力值 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an。
需要进行 n − 1 n−1 n−1轮比赛,每一轮在剩下的玩家里任选两个玩家 i , j i,j i,j。如果 ∣ a i − a j ∣ > K |a_i−a_j|>K ∣ai−aj∣>K,那么其中能力值高的玩家会获胜,能力值低的玩家会被淘汰。如果 ∣ a i − a j ∣ ≤ K |ai−aj|≤K ∣ai−aj∣≤K,那么两个玩家都有可能获胜,另一个玩家被淘汰。
n − 1 n−1 n−1轮比赛之后,只剩下一个玩家。问有多少个玩家可能是最后获胜的玩家。
第一行,两个整数 n , K n,K n,K,表示玩家的总人数,和获胜条件中的参数。
接下来一行 n n n个整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an,表示玩家的能力值。
一个整数,表示最后可能获胜的玩家个数。
5 3
1 5 9 6 3
5
见下发文件。
共 10 10 10组数据。
测试点 1 1 1满足 n ≤ 5 n≤5 n≤5。
测试点 2 2 2满足 n ≤ 10 n≤10 n≤10。
测试点 3 , 4 , 5 3,4,5 3,4,5满足 n ≤ 1000 n≤1000 n≤1000。
对于 100 100 100%的数据,满足 n ≤ 1 0 5 n≤10^5 n≤105, 1 ≤ a i , K ≤ 1 0 9 1≤a_i,K≤10^9 1≤ai,K≤109。
能力值越高的越容易获胜,这是显然的
如果我们要计算最多有多少人可能获胜,自然要求出能获胜的最低能力值是多少
题目中给出了一个能够以弱胜强的最大能力差值 K K K,能力差值大于 K K K的时候弱的一方不可能获胜
所以我们让能力值大的先开始比赛,每次比赛都让能力值小的一方获胜(前提是能获胜)
最后当能力值小的一方不可能获胜的时候,我们就得到了最多有多少人可能获胜
AC代码如下
#include
#include
using namespace std;
const int max_n = 1e5;
const int max_a = 1e9;
const int max_k = 1e9;
int n;
long long k, gamer[max_n + 1];
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> gamer[i];
}
sort(gamer + 1, gamer + 1 + n, greater<long long>());
long long ans = 1;
long long buffer = gamer[1];
for (int i = 2; i <= n; i++) {
if (buffer - gamer[i] <= k) {
ans++;
buffer = gamer[i];
}
else break;
}
cout << ans << endl;
return 0;
}
请按字典序从小到大的顺序输出所有序列,满足序列中有 p 1 p_1 p1 个 1 1 1, p 2 p_2 p2 个 2 2 2, . . . ... ... , p n p_n pn 个 n n n。
第一行一个整数 n n n。
第二行 n n n 个整数 p 1 p_1 p1, p 2 p_2 p2,…, p n p_n pn。
按字典序从小到大的顺序一行一行输出所有满足条件的序列,每行一个序列,相邻两个数字需要用空格隔开。
3
1 2 2
1 2 2 3 3
1 2 3 2 3
1 2 3 3 2
1 3 2 2 3
1 3 2 3 2
1 3 3 2 2
2 1 2 3 3
2 1 3 2 3
2 1 3 3 2
2 2 1 3 3
2 2 3 1 3
2 2 3 3 1
2 3 1 2 3
2 3 1 3 2
2 3 2 1 3
2 3 2 3 1
2 3 3 1 2
2 3 3 2 1
3 1 2 2 3
3 1 2 3 2
3 1 3 2 2
3 2 1 2 3
3 2 1 3 2
3 2 2 1 3
3 2 2 3 1
3 2 3 1 2
3 2 3 2 1
3 3 1 2 2
3 3 2 1 2
3 3 2 2 1
对于 100 100 100% 的数据,保证 1 ≤ n ≤ 9 1≤n≤9 1≤n≤9, 1 ≤ p i ≤ 9 1≤p_i≤9 1≤pi≤9,保证满足条件的序列个数不超过 1 0 5 10^5 105 个。
这里我们隆重介绍一个新的偷懒工具,next_permutation
和prev_permutation
传入的参数是数组的起止位置指针,功能是对数组本身操作,得到下一个/上一个字典序排列
然后怎么做就不用我说了吧 q w q qwq qwq
AC代码如下
#include
#include
using namespace std;
void read(string& str) {
int n1, n2;
cin >> n1;
for (int i = 1; i <= n1; i++) {
cin >> n2;
str.append(n2, i + '0');
}
}
int main() {
string str = ""; read(str);
int len = str.size();
do {
for (int i = 0; i < len; i++) printf("%c ", str[i]);
printf("\n");
}
while (next_permutation(str.begin(), str.end()));
return 0;
}
让我看看是谁不会进制转换,哦原来是我
以不同进制的形式输入 n n n 个非负整数,求出它们的和并以 m m m 进制的形式输出。
使用大写字母 A
~ Z
依次代表 10 10 10 ~ 35 35 35, 小写字母 a
~ z
依次代表 36 36 36 ~ 61 61 61。
第一行输入两个整数 1 ≤ n ≤ 10 1≤n≤10 1≤n≤10 , 2 ≤ m ≤ 62 2≤m≤62 2≤m≤62 。
接下来 n n n 行,每行输入一个整数 2 ≤ t ≤ 62 2≤t≤62 2≤t≤62, 一个 t t t 进制数 0 ≤ x ≤ 1 0 9 0≤x≤10^9 0≤x≤109。
一个 m m m 进制数,为最终的结果
2 2
2 1
6 10
111
1 10
52 aA0
97864
2 52
36 AMD
52 YES
dJD
由任意进制转换为十进制:
遍历每一位上的数字,乘以其位权重,累加
由十进制转换为任意进制:
对十进制数进行取模,余数插入转换后结果的左侧,然后将十进制数除以 10 10 10,循环操作直到十进制数为 0 0 0
AC代码如下
#include
#include
#include
using namespace std;
const int max_n = 10;
const int max_m = 62;
const int max_t = 62;
const int max_len = 64;
const string sign_set = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int n, t;
long long m;
string num;
map<char, int>convert;
char output[max_len + 1];
void init() {
int len = sign_set.size();
for (int i = 0; i < len; i++) {
convert.insert(pair<char, int>(sign_set[i], i));
}
}
int main() {
init();
cin >> n >> m;
long long decimal = 0;
for (int i = 0; i < n; i++) {
cin >> t >> num;
int p = 1;
int len = num.size() - 1;
for (int i = len; i >= 0; i--) {
decimal += (long long)(convert[num[i]] * p);
p *= t;
}
}
int idx = max_len - 1;
while (decimal != 0) {
output[idx--] = sign_set[decimal % m];
decimal /= m;
}
cout << &(output[++idx]) << endl;
return 0;
}
一个字符串 S S S是另一字符串 T T T的循环子串当且仅当存在 k k k, T T T所有字符循环右移 k k k位后得到的新串 T ′ T′ T′,满足 S S S是 T ′ T′ T′的子串。
例如: abc
是 cefab
的循环子串。 (cefab
循环右移 2 2 2位得到abcef
, abc
是abcef
的子串)
一个串 P P P是完全循环串当且仅当对于它的任一子串 H H H, 都有 H r e v e r s e Hreverse Hreverse是 P P P的循环子串 ( H r e v e r s e Hreverse Hreverse 为 H H H的倒转, 如abc
r e v e r s e reverse reverse后 为cba
)。
给一个长度为 n n n的字符串, 判断它是不是完全循环串。
第一行一个正整数 t t t, 表示测试数据组数。
对于每一组数据,第一行一个正整数 n n n, 表示字符串的长度。接下来一行一个长度为 n n n的字符串. 仅包含小写字母。
对于每组测试数据,如果这个串是完全循环串, 输出YES
,否则输出NO
。每组测试数据之间输出换行。
对于所有数据 有 1 ≤ t ≤ 100 1≤t≤100 1≤t≤100, 1 ≤ n ≤ 1 0 3 1≤n≤10^3 1≤n≤103, ∑ n ≤ 1 0 3 \sum n≤10^3 ∑n≤103。
2
4
ccca
11
eeaafbddfaa
YES
NO
本道题目只需要语法知识就可以解决。
任意子串是什么意思呢?
如果一个子串包含另一个子串,那么我们是不是只需要求出大子串的合法情况,就可以推出小子串的合法情况。
从大的子串向小的子串考虑 最大的子串是什么呢?
假设abc
是循环子串,那么cba
也应该是循环子串,则有***abc***cba***
*注:这里将abc
与cba
调换也可以,*
的数量可以是任意的
进一步假设abcd
是循环子串,则有***abcd*dcba***
不难发现,如果abcd
是循环子串,则abc
自然也是循环子串
所以我们只需要证明最大的循环子串符合条件即可
也就是将给定字符串反转,然后寻找其是否存在
而对于可以循环右移的字符串,我们只需要将其复制然后拼接,在处理之后的字符串中寻找即可
AC代码如下
#include
#include
#include
using namespace std;
const int max_t = 100;
const int max_n = 1e3;
string str;
int t, len;
int main() {
cin >> t;
for (int i = 0; i < t; i++) {
cin >> len >> str;
string rev = str;
reverse(rev.begin(), rev.end());
str = str + str;
if (str.find(rev) != -1)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
故事接着《饿饿 饭饭 2》,又过了几个月,暑假来啦!!!
这天, c c cc cc和他的小伙伴们决定一起去游乐园玩,他们一天将游乐园的所有设施玩了个遍,甚至大摆锤,过山车他们还去了很多次,愉快的时间总是很短暂的,很快时间就来到了晚上,但是你以为一天的娱乐时光就这样结束了吗,那你就猜错啦。
晚上,游乐园晚上的 p a r t y party party就开始啦,其中有一个游戏环节,赢的人可以得到免费的西瓜,饿到不行的 c c cc cc和他的小伙伴非常希望得到这个西瓜。
包括 c c cc cc和他的小伙伴,有 t t t个玩家参与了这个游戏,每个玩家都有一张带有数字的卡片。第 i i i张卡片上有 n i n_i ni个数字,分别是 m 1 , m 2 , . . . m n m_1,m_2,...m_n m1,m2,...mn。
游戏过程中,主持人从袋子里一个一个地取出编号的球。 他用洪亮而清晰的声音大声念出球的编号,然后把球收起来。 如果玩家的卡片上有对应的数字,就可以将它划掉。 最先从他的卡片上划掉所有数字的人获胜。 如果多人同时从他们的卡片上划掉所有数字,那么这些人都不能赢得比赛。 在游戏开始时,袋子里有 100 100 100 个球,编号从 1 1 1 到 100 100 100,所有球的编号都是不同的。
c c cc cc偷偷知道了每个玩家的数字。 想请你确定每个玩家是否可以在最有利于他的情况下赢得比赛。
第一行给出一个数 t t t,代表 t t t个玩家。
接下来第二行到 t + 1 t+1 t+1行,每行第一个数为 n i n_i ni,代表这个人手中有 n n n个卡片,接下来给出序列 a 1 . . . a n a_1...a_n a1...an表示这个人所拥有的卡片的数字。
输出 t t t行,每一行给出第 i i i个人在最有利的情况下是否能赢得比赛,可以输出YES
, 不可以输出NO
。
1 ≤ t ≤ 100 , 1 ≤ n ≤ 100 , 1 ≤ m i ≤ 100 1≤t≤100,1≤n≤100,1≤m_i≤100 1≤t≤100,1≤n≤100,1≤mi≤100
3
1 1
3 2 4 1
2 10 11
YES
NO
YES
不难发现,如果有一个人不能获胜,那么一定是有其他人的数字集合是他的子集(注:由于可以一次性划去多个数字,重复个数字直接按单个数字处理即可)
那么采用最简单的判断思路——暴力搜索
计算一下时间复杂度:最多需要判断 100 100 100个人,对于每个人需要搜索其余 99 99 99个人,每人最多有 100 100 100个数字,所以时间复杂度上限是 1 0 6 10^6 106,可以接受
所以我们可以采用二维bool
数组存储每一个人数字,然后进行||
的操作:
num_arr[i][k] != (num_arr[i][k] || num_arr[j][k])
当且仅当num_arr[i][k]
没有k
数字(为 f a l s e false false),且num_arr[j][k]
有k
数字(为 t r u e true true)时表达式为真
如果表达式为真,就可以停止判断了,因为二者有不同的元素;如果表达式始终为假,那么就说明j
的数字集合是i
的子集,i
不可能获胜
AC代码如下
#include
using namespace std;
const int max_n = 100;
const int max_len = 100;
bool num_arr[max_n + 1][max_len + 1];
int n, m;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> m;
int num;
for (int j = 0; j < m; j++) {
cin >> num;
num_arr[i][num] = true;
}
}
for (int i = 1; i <= n; i++) {
bool win = true;
for (int j = 1; j <= n; j++) {
if (i == j) continue;
int k;
for (k = 1; k <= max_len; k++) {
if (num_arr[i][k] != (num_arr[i][k] || num_arr[j][k])) {
break;
}
}
if (k == max_len + 1) {
win = false;
break;
}
}
if (win) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}