前言
P9670 [ICPC 2022 Jinan R] Frozen Scoreboard
题目描述
2000 年以前的秦朝,曾举办过一次 ICPC 比赛。比赛中有 m m m 道题, n n n 个团队。我们知道每个队完成了多少道题以及其历史记录的总用时。这些称作该团队的结果,但是我们不知道他们每道题是否完成、用时多久。
最近,我们发现了每个队冻结的计分板。从该计分板上,我们可以看到每个队在比赛中的提交情况,但是不知道在最后一小时内提交的判分。一些人发现,对于一些队来说,他们冻结的计分板可能与他们在历史记录中的最终成绩相矛盾。
请根据最终得分和冻结的计分板,为各队创建一个与其最终结果和冻结的计分板一致的最终计分板。
按照以下规则来计算计分板和总分:
对于给定的队伍 i i i,它最终的计分板 是一个 m m m 元数组,其中第 j j j 个元素给出队伍 i i i 在第 j j j 题上的提交信息。
-
如果队伍 i i i 没有提交问题 j j j,输出
.。 -
如果队伍 i i i 对问题 j j j 提交了 x x x 次但均未通过,输出 − x -x −x。
-
否则,考虑队伍 i i i 在问题 j j j 的所有评测结果。每次提交都有一个提交时间,设第一个通过的评测是第 x x x 次评测,在第 y y y 分钟时提交。输出 + x / y +x/y +x/y,其中 0 ≤ y ≤ 299 0\leq y\leq299 0≤y≤299。
在最终计分板上,只考虑第一次通过的提交。同一分钟内可能有多次提交。
一个队伍的最终得分是该队伍完成了多少道题,即该队最终计分板上 +的个数。
一个队伍总用时按如下方式计算。如果队伍 i i i 在第 y y y 分钟完成了第 j j j 道题,在完成前有 x − 1 x-1 x−1 次失败的提交(即最终计分板上第 j j j 个问题的数为 + x / y +x/y +x/y),该问题的用时记为 20 ( x − 1 ) + y 20(x-1)+y 20(x−1)+y。 如果队伍 i i i 没有完成第 j j j 道题,该问题的用时记为 0 0 0,无论是否提交过。队伍 i i i 的总时间是每道题用时的总和。
输入格式
第一行包括两个整数 n , m ( 1 ≤ n ≤ 1000 , 1 ≤ m ≤ 13 ) n,m\;(1\leq n\leq1000,1\leq m\leq13) n,m(1≤n≤1000,1≤m≤13),为队伍个数和题目个数。
接下来 n n n 组,描述每个队伍的最终得分和冻结的计分板。
第 i i i 组表示队伍 i i i。每一组中,第一行包括两个整数 a i , b i ( 0 ≤ a i ≤ m , 0 ≤ b i ≤ 10 5 ) a_i,b_i\;(0\leq a_i\leq m,0\leq b_i\leq10^5) ai,bi(0≤ai≤m,0≤bi≤105),为队伍 i i i 在整场比赛中完成的题目个数和每道题的用时。这两个数字是比赛的最终结果。
接下来 m m m 行,描述队伍 i i i 在计分板上的内容。对于任意 1 ≤ j ≤ m 1\leq j\leq m 1≤j≤m,
-
如果第 j j j 行是 + x / y ( 1 ≤ x ≤ 100 , 0 ≤ y ≤ 239 ) +\,x/y\;(1\leq x\leq100,0\leq y\leq239) +x/y(1≤x≤100,0≤y≤239),表示队伍 i i i 在第 y y y 分钟,第 x x x 次提交时通过了题 j j j。
-
如果第 j j j 行是 ? x y ( 1 ≤ x ≤ y ≤ 100 ) ?\,x\,y\;(1\leq x\leq y\leq100) ?xy(1≤x≤y≤100),表示队伍 i i i 没有在前四个小时中作出题 j j j。这个队伍提交了 y y y 次,其中 x x x 次在最后一小时内。最后一小时内且通过该题的提交记录会在冻结的计分板 上显示,但不会在最终计分板上显示。
-
如果第 j j j 行是 − x -x −x,表示队伍 i i i 没有在前四小时内作出题 j j j。这个队伍在最后一个小时前提交了 x ( 1 ≤ x ≤ 100 ) x\,(1\leq x\leq100) x(1≤x≤100) 次,且没有在最后一小时内做出题 j j j。
-
如果第 j j j 行是一个单一的字符
.,表示队伍 i i i 没有提交过题 j j j。
输出格式
对于每个队伍 i i i,如果最终结果和冻结的计分板相矛盾,输出一行 No \texttt{No} No。否则,第一行输出 Yes \texttt{Yes} Yes,接下来 m m m 行,描述一种队伍 i i i 可能的计分板,满足最终结果和冻结的计分板。其中,第 j j j 行应该包括:
-
- x / y ( 1 ≤ x ≤ 100 , 0 ≤ y ≤ 299 ) +\,x/y\,(1\leq x\leq100,0\leq y\leq299) +x/y(1≤x≤100,0≤y≤299),如果队伍 i i i 在第 x x x 次提交,第 y y y 分钟完成了题 j j j,在这之前没有队伍通过这道题。不要在字符
/前后输出多余的空格。
- x / y ( 1 ≤ x ≤ 100 , 0 ≤ y ≤ 299 ) +\,x/y\,(1\leq x\leq100,0\leq y\leq299) +x/y(1≤x≤100,0≤y≤299),如果队伍 i i i 在第 x x x 次提交,第 y y y 分钟完成了题 j j j,在这之前没有队伍通过这道题。不要在字符
-
− x ( 1 ≤ x ≤ 100 ) -x\,(1\leq x\leq100) −x(1≤x≤100),如果队伍 i i i 提交了 x x x 次题 j j j,且均未通过。
-
.,如果队伍 i i i 没有提交题 j j j。
如果有多种可能的答案,任意输出一种即可。
请注意,在输入和输出中,?,+,-后总有一个空格。
输入输出样例 #1
输入 #1
1 13
7 951
+ 1/6
? 3 4
+ 4/183
- 2
+ 3/217
.
.
.
+ 2/29
+ 1/91
.
+ 1/22
.
输出 #1
Yes
+ 1/6
+ 2/263
+ 4/183
- 2
+ 3/217
.
.
.
+ 2/29
+ 1/91
.
+ 1/22
.
输入输出样例 #2
输入 #2
6 2
1 100
.
? 3 4
2 100
+ 1/1
+ 1/2
0 0
- 5
- 6
2 480
? 100 100
? 100 100
2 480
? 99 100
? 100 100
1 2000
? 100 100
? 100 100
输出 #2
No
No
Yes
- 5
- 6
Yes
+ 1/240
+ 1/240
No
Yes
+ 87/280
- 100
模拟
点、加号和减号原样输出。
解析+的x,y。n1++,t1+= 20 × ( x − 1 ) + y 20\times(x-1)+y 20×(x−1)+y
枚举带?的题的对错,不超过m道。故状态数不超过 2 m 2^m 2m。
令做对了n2题,最少用时t21,最多用时t22。
如果 n 1 + n 2 = = a 且 ( t 1 + t 21 ) ≥ b 且 ( t 1 + t 22 ) ≤ b n1+n2==a 且 (t1+t21) \geq b 且(t1+t22) \leq b n1+n2==a且(t1+t21)≥b且(t1+t22)≤b
计算t11:第 ( y − x ) + 1 (y-x)+1 (y−x)+1次于240分提交通过。
计算t12:第 y y y次于299分提交。
如果分配b
以t11为基础,多的时间如果超过20,则提交数增加。
提交次数到上限了,则延长提交时间。
如果还有剩余。返回冲突。
代码
核心代码
cpp
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>
#include <bitset>
using namespace std;
template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
in >> pr.first >> pr.second;
return in;
}
template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
in >> get<0>(t) >> get<1>(t) >> get<2>(t);
return in;
}
template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
return in;
}
template<class T1, class T2, class T3, class T4, class T5, class T6, class T7 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4,T5,T6,T7>& t) {
in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t) >> get<4>(t) >> get<5>(t) >> get<6>(t);
return in;
}
template<class T = int>
vector<T> Read() {
int n;
cin >> n;
vector<T> ret(n);
for (int i = 0; i < n; i++) {
cin >> ret[i];
}
return ret;
}
template<class T = int>
vector<T> ReadNotNum() {
vector<T> ret;
T tmp;
while (cin >> tmp) {
ret.emplace_back(tmp);
if ('\n' == cin.get()) { break; }
}
return ret;
}
template<class T = int>
vector<T> Read(int n) {
vector<T> ret(n);
for (int i = 0; i < n; i++) {
cin >> ret[i];
}
return ret;
}
template<int N = 1'000'000>
class COutBuff
{
public:
COutBuff() {
m_p = puffer;
}
template<class T>
void write(T x) {
int num[28], sp = 0;
if (x < 0)
*m_p++ = '-', x = -x;
if (!x)
*m_p++ = 48;
while (x)
num[++sp] = x % 10, x /= 10;
while (sp)
*m_p++ = num[sp--] + 48;
AuotToFile();
}
void writestr(const char* sz) {
strcpy(m_p, sz);
m_p += strlen(sz);
AuotToFile();
}
inline void write(char ch)
{
*m_p++ = ch;
AuotToFile();
}
inline void ToFile() {
fwrite(puffer, 1, m_p - puffer, stdout);
m_p = puffer;
}
~COutBuff() {
ToFile();
}
private:
inline void AuotToFile() {
if (m_p - puffer > N - 100) {
ToFile();
}
}
char puffer[N], * m_p;
};
template<int N = 1'000'000>
class CInBuff
{
public:
inline CInBuff() {}
inline CInBuff<N>& operator>>(char& ch) {
FileToBuf();
while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车
ch = *S++;
return *this;
}
inline CInBuff<N>& operator>>(int& val) {
FileToBuf();
int x(0), f(0);
while (!isdigit(*S))
f |= (*S++ == '-');
while (isdigit(*S))
x = (x << 1) + (x << 3) + (*S++ ^ 48);
val = f ? -x : x; S++;//忽略空格换行
return *this;
}
inline CInBuff& operator>>(long long& val) {
FileToBuf();
long long x(0); int f(0);
while (!isdigit(*S))
f |= (*S++ == '-');
while (isdigit(*S))
x = (x << 1) + (x << 3) + (*S++ ^ 48);
val = f ? -x : x; S++;//忽略空格换行
return *this;
}
template<class T1, class T2>
inline CInBuff& operator>>(pair<T1, T2>& val) {
*this >> val.first >> val.second;
return *this;
}
template<class T1, class T2, class T3>
inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {
*this >> get<0>(val) >> get<1>(val) >> get<2>(val);
return *this;
}
template<class T1, class T2, class T3, class T4>
inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {
*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);
return *this;
}
template<class T = int>
inline CInBuff& operator>>(vector<T>& val) {
int n;
*this >> n;
val.resize(n);
for (int i = 0; i < n; i++) {
*this >> val[i];
}
return *this;
}
template<class T = int>
vector<T> Read(int n) {
vector<T> ret(n);
for (int i = 0; i < n; i++) {
*this >> ret[i];
}
return ret;
}
template<class T = int>
vector<T> Read() {
vector<T> ret;
*this >> ret;
return ret;
}
private:
inline void FileToBuf() {
const int canRead = m_iWritePos - (S - buffer);
if (canRead >= 100) { return; }
if (m_bFinish) { return; }
for (int i = 0; i < canRead; i++)
{
buffer[i] = S[i];//memcpy出错
}
m_iWritePos = canRead;
buffer[m_iWritePos] = 0;
S = buffer;
int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);
if (readCnt <= 0) { m_bFinish = true; return; }
m_iWritePos += readCnt;
buffer[m_iWritePos] = 0;
S = buffer;
}
int m_iWritePos = 0; bool m_bFinish = false;
char buffer[N + 10], * S = buffer;
};
class Solution {
public:
vector<string> Ans(int rcnt,int time, vector<string>& strs) {
int n1 = 0, t1 = 0;
vector<tuple<int, int,int>> v;
for (const auto& s : strs) {
if ('+' == s[0]) {
int pos1 = s.find('/');
const int x = atoi(s.c_str() + 2);
const int y = atoi(s.c_str() + pos1 + 1);
n1++;
t1 += (x - 1) * 20 + y;
}
else if ('?' == s[0]) {
auto tmp = s.substr(2);
const int pos = tmp.find(' ');
const int x = atoi(tmp.c_str() );
const int y = atoi(tmp.c_str() + pos + 1);
v.emplace_back(y, y - x + 1,240);
}
}
if (v.empty()) {
if ((n1 != rcnt) || (time != t1)) { return {}; }
return strs;
}
const int M = 1 << v.size();
for (int m = 0; m < M; m++)
{
int i = -1;
int n2 = 0, t21 = 0, t22 = 0;
for (const auto& [may, miy,tmp] : v) {
i++;
if ((1 << i) & m) { continue; }
n2++;
t21 += (miy - 1) * 20 + 240;
t22 += (may - 1) * 20 + 299;
}
if ((n1 + n2 != rcnt)) { continue; }
if ((time < t1 + t21) || (time > t1 + t22)) { continue; }
int remain = time - t1 - t21;
i = -1;
for (auto& [may, miy, tmp] : v) {
i++;
if ((1 << i) & m) { continue; }
const int i1 = min(may - miy, remain / 20);
miy += i1; remain -= i1 * 20;
}
i = -1;
for (auto& [may, miy, tmp] : v) {
i++;
if ((1 << i) & m) { continue; }
const int i1 = min(59, remain);
tmp += i1; remain -=i1;
}
vector<string> str2;
i = -1;
for (auto& [may, miy, tmp] : v) {
i++;
if ((1 << i) & m) { str2.emplace_back("- " + to_string(may)); continue; }
str2.emplace_back("+ " + to_string(miy) +"/" + to_string(tmp));
}
for (int i = 0, j = 0; i < strs.size(); i++) {
if ('?' == strs[i][0]) {
strs[i] = str2[j++];
}
}
return strs;
}
return {};
}
};
int main() {
#ifdef _DEBUG
freopen("a.in", "r", stdin);
#endif // DEBUG
ios::sync_with_stdio(0); cin.tie(nullptr);
//CInBuff<> in; COutBuff<10'000'000> ob;
int N,M,rcnt,time;
cin >> N >> M ;
char buf[1000] = { 0 };
for (int i = 0; i < N; i++)
{
cin >> rcnt >> time;
cin.getline(buf, 999);
vector<string> strs;
for (int j = 0; j < M; j++) {
cin.getline(buf, 999);
strs.emplace_back(buf);
}
auto res= Solution().Ans(rcnt,time,strs);
#ifdef _DEBUG
printf("rcnt=%d,time=%d", rcnt,time);
Out(strs, ",strs=");
//Out(lr, ",lr=");
#endif // DEBUG
if (res.empty()) {
cout << "No" << "\n";
}
else {
cout << "Yes" << "\n";
for (const auto& i: res) {
cout << i << "\n";
}
}
}
return 0;
};
单元测试
cpp
int rcnt, time;
vector<string> strs;
void Check(int rcnt, int time, vector<string> strs) {
int n1 = 0, t1 = 0;
for (const auto& s : strs) {
if ('+' == s[0]) {
int pos1 = s.find('/');
const int x = atoi(s.c_str() + 2);
const int y = atoi(s.c_str() + pos1 + 1);
n1++;
t1 += (x - 1) * 20 + y;
}
}
AssertEx(rcnt, n1);
AssertEx(time, t1);
}
TEST_METHOD(TestMethod11)
{
rcnt = 7, time = 951, strs = { "+ 1/6","? 3 4","+ 4/183","- 2","+ 3/217",".",".",".","+ 2/29","+ 1/91",".","+ 1/22","."};
auto res = Solution().Ans(rcnt,time, strs);
Check(rcnt, time, res);
}
TEST_METHOD(TestMethod12)
{
rcnt = 1, time = 100, strs = { ".","? 3 4" };
auto res = Solution().Ans(rcnt, time, strs);
AssertEx({}, res);
}
TEST_METHOD(TestMethod13)
{
rcnt = 2, time = 100, strs = { "+ 1/1","+ 1/2" };
auto res = Solution().Ans(rcnt, time, strs);
AssertEx({}, res);
}
TEST_METHOD(TestMethod14)
{
rcnt = 2, time = 480, strs = { "? 99 100","? 100 100" };
auto res = Solution().Ans(rcnt, time, strs);
AssertEx({}, res);
}
TEST_METHOD(TestMethod21)
{
rcnt = 0, time = 0, strs = { "- 5","- 6" };
auto res = Solution().Ans(rcnt, time, strs);
Check(rcnt, time, res);
}
TEST_METHOD(TestMethod22)
{
rcnt = 2, time = 480, strs = { "? 100 100","? 100 100" };
auto res = Solution().Ans(rcnt, time, strs);
Check(rcnt, time, res);
}
TEST_METHOD(TestMethod23)
{
rcnt = 1, time = 2000, strs = { "? 100 100","? 100 100" };
auto res = Solution().Ans(rcnt, time, strs);
Check(rcnt, time, res);
}

扩展阅读
| 我想对大家说的话 |
|---|
| 工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
| 学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
| 有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
| 闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
| 子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
| 如果程序是一条龙,那算法就是他的是睛 |
| 失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法 用**C++**实现。
