本文同步发表于知乎:https://www.zhihu.com/question/10175007563/answer/88432302849
Deepseek R1 在宣传的时候说算法竞赛能力与 Openai-o1 相当,CF Rating 为 2029,稍低于 Openai-o1 的 2061(参考链接:https://github.com/deepseek-ai/DeepSeek-R1)
于是我想着,让 Deepseek R1 来做去年 CSP-S 和 NOIP 的题(关闭联网),看看能不能打到全国一等基准线,结果上来就给我拉了坨大的。思考的 prompt 为:
你是一位训练有素的算法竞赛选手,请使用 C++ 语言完成这一个算法竞赛题,测试点时间限制 1s,内存限制 512MB。
以决斗为例,复制题面输入后,思考了 883 秒,然后生成了这个代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
vector<int> r(n);
for (int i = 0; i < n; ++i) {
cin >> r[i];
}
sort(r.begin(), r.end());
int count = 0;
int i = n - 1, j = i - 1;
while (i >= 0 && j >= 0) {
if (r[i] > r[j]) {
count++;
i--;
j--;
} else {
j--;
}
if (j < 0) {
i--;
if (i >= 0) j = i - 1;
}
}
cout << (n - count) << endl;
return 0;
}
告知只能获得 40 分,要求重新检查后,又思考了 710 秒,生成了这个代码(这两份代码有什么区别?):
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
vector<int> r(n);
for (int i = 0; i < n; ++i) {
cin >> r[i];
}
sort(r.begin(), r.end());
int count = 0;
int i = n - 1, j = i - 1;
while (i >= 0 && j >= 0) {
if (r[i] > r[j]) {
count++;
i--;
j--;
} else {
j--;
}
if (j < 0) {
i--;
if (i >= 0) j = i - 1;
}
}
cout << (n - count) << endl;
return 0;
}
接着测试了超速检测。可能是超速检测的做法更经典,Deepseek R1 只思考了 186 秒,就写出了这个代码,获得了 60 分:
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
typedef pair<int, int> pii;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while (T--) {
int n, m, L, V;
cin >> n >> m >> L >> V;
vector<int> p(m);
vector<pair<int, int>> intervals;
vector<int> d(n), v(n), a(n);
for (int i = 0; i < n; ++i) {
cin >> d[i] >> v[i] >> a[i];
}
for (int j = 0; j < m; ++j) {
cin >> p[j];
}
int ans1 = 0;
for (int i = 0; i < n; ++i) {
int di = d[i], vi = v[i], ai = a[i];
double S_i;
if (ai >= 0) {
S_i = L - di;
if (S_i < 0) S_i = 0;
} else {
double s0_stop = (vi * 1.0 * vi) / (2.0 * (-ai));
double max_s = L - di;
S_i = min(s0_stop, max_s);
if (S_i < 0) S_i = 0;
}
if (S_i <= 0) continue;
bool valid = false;
double l, r;
bool left_closed, right_closed;
if (ai == 0) {
if (vi > V) {
valid = true;
left_closed = true;
right_closed = true;
l = di;
r = L;
} else {
valid = false;
}
} else if (ai > 0) {
if (vi >= V) {
valid = true;
left_closed = true;
right_closed = true;
l = di;
r = di + S_i;
} else {
double s0 = (V*1.0*V - vi*vi) / (2.0 * ai);
if (s0 > S_i) {
valid = false;
} else {
valid = true;
left_closed = false;
right_closed = true;
l = di + s0;
r = di + S_i;
}
}
} else {
if (vi <= V) {
valid = false;
} else {
double s0_speed = (V*1.0*V - vi*vi) / (2.0 * ai);
double s0_stop = (vi*1.0 * vi) / (2.0 * (-ai));
double max_s = L - di;
double actual_s = min(s0_stop, max_s);
double actual_s_speed = min(s0_speed, actual_s);
if (actual_s_speed <= 0) {
valid = false;
} else {
valid = true;
left_closed = true;
right_closed = false;
l = di;
r = di + actual_s_speed;
}
}
}
if (!valid) continue;
vector<int>::iterator start_it, end_it;
if (left_closed) {
start_it = lower_bound(p.begin(), p.end(), l);
} else {
start_it = upper_bound(p.begin(), p.end(), l);
}
if (right_closed) {
end_it = upper_bound(p.begin(), p.end(), r);
} else {
end_it = lower_bound(p.begin(), p.end(), r);
}
int start_idx = start_it - p.begin();
int end_idx = (end_it - p.begin()) - 1;
if (start_idx <= end_idx) {
ans1++;
intervals.emplace_back(start_idx, end_idx);
}
}
int ans2 = 0;
if (!intervals.empty()) {
sort(intervals.begin(), intervals.end(), [](const pii& a, const pii& b) {
return a.second < b.second;
});
int last = -1;
int count = 0;
for (const auto& interval : intervals) {
int s = interval.first;
int e = interval.second;
if (s > last) {
count++;
last = e;
}
}
ans2 = m - count;
} else {
ans2 = m;
}
cout << ans1 << ' ' << ans2 << '\n';
}
return 0;
}
超速检测这一题我没有给予更多的测试。很明显,Deepseek R1 在 CSP-S 2024 中表现得很惨淡,前两个题没有一个能一次通过,甚至在非常简单的决斗一题中表现特别不理想。这个水平不像是具有 CF Rating 2000 的选手。
随后测试了 NOIP 2024。NOIP 2024 的难度是更高的,但是 Openai-o1 的表现还算不错,我还是对 Deepseek 的表现比较期待。结果实际上的表现很不尽如人意。
编辑字符串是一道贪心+细节题,其难度不低,有一些选手在这个题上翻车。首先,它思考了 292 秒,生成了如下代码,这份代码取得了 0 分的高分:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void preprocess(const string &s, const string &t, vector<int> &count0, vector<int> &count1) {
int n = s.size();
int current_start = -1;
int cnt0 = 0, cnt1 = 0;
for (int i = 0; i < n; ++i) {
if (t[i] == '1') {
if (current_start == -1) {
current_start = i;
cnt0 = 0;
cnt1 = 0;
}
if (s[i] == '0') cnt0++;
else cnt1++;
} else {
if (current_start != -1) {
for (int j = current_start; j < i; ++j) {
count0[j] = cnt0;
count1[j] = cnt1;
}
current_start = -1;
}
}
}
if (current_start != -1) {
for (int j = current_start; j < n; ++j) {
count0[j] = cnt0;
count1[j] = cnt1;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
string s1, s2, t1, t2;
cin >> s1 >> s2 >> t1 >> t2;
vector<int> c1_0(n, 0), c1_1(n, 0);
preprocess(s1, t1, c1_0, c1_1);
vector<int> c2_0(n, 0), c2_1(n, 0);
preprocess(s2, t2, c2_0, c2_1);
int ans = 0;
for (int i = 0; i < n; ++i) {
bool a_fixed = (t1[i] == '0');
bool b_fixed = (t2[i] == '0');
if (a_fixed && b_fixed) {
if (s1[i] == s2[i]) ans++;
} else if (a_fixed) {
char c = s1[i];
if (c == '0' && c2_0[i] > 0) ans++;
else if (c == '1' && c2_1[i] > 0) ans++;
} else if (b_fixed) {
char c = s2[i];
if (c == '0' && c1_0[i] > 0) ans++;
else if (c == '1' && c1_1[i] > 0) ans++;
} else {
if ((c1_0[i] > 0 && c2_0[i] > 0) || (c1_1[i] > 0 && c2_1[i] > 0)) {
ans++;
}
}
}
cout << ans << '\n';
}
return 0;
}
给出的解释是:
方法思路
预处理块结构:将每个字符串分割成由不可交换字符分隔的可交换块,并统计每个块中的 0 和 1 的数量。
判断每个位置的可匹配性:根据每个位置的可交换性,判断是否可以通过调整使得该位置的字符相同。
具体步骤如下:
预处理块结构:遍历每个字符串的可交换标记,确定每个块的范围及其字符统计信息。
逐个位置判断:根据每个位置的可交换性和对应块的字符统计信息,判断该位置是否可以调整为相同字符。
然后,告知了它大样例中满足字符串长度不超过 10 的前两组数据,然后 R1 连续思考了 30 分钟直接被掐断。
遗失的赋值是一道组合计数题,这一题 Openai-o1 可以通过,而 Deepseek R1 经过了 552 秒的思考生成了这个代码,获得了 35 分。它有意识地去拆了贡献去计算答案,但是很遗憾思考流程貌似是有问题的。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
ll pow_mod(ll a, ll b) {
ll res = 1;
a %= MOD;
while (b > 0) {
if (b % 2 == 1) res = (res * a) % MOD;
a = (a * a) % MOD;
b /= 2;
}
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n, m, v;
cin >> n >> m >> v;
unordered_map<int, int> fixed;
bool conflict = false;
for (int i = 0; i < m; ++i) {
int c, d;
cin >> c >> d;
if (fixed.count(c)) {
if (fixed[c] != d) conflict = true;
} else {
fixed[c] = d;
}
}
if (conflict) {
cout << 0 << '\n';
continue;
}
vector<int> sorted_c;
for (auto& p : fixed) sorted_c.push_back(p.first);
sort(sorted_c.begin(), sorted_c.end());
int cnt = 0;
for (int i = 1; i < sorted_c.size(); ++i) {
if (sorted_c[i] == sorted_c[i-1] + 1) cnt++;
}
ll k = (1 + (ll)v * (v - 1)) % MOD;
ll term1 = pow_mod(k, cnt);
ll exponent = n - 1 - cnt;
ll v_sq = ((ll)v % MOD) * (v % MOD) % MOD;
ll term2 = pow_mod(v_sq, exponent);
ll ans = (term1 * term2) % MOD;
cout << ans << '\n';
}
return 0;
}
我最后测了一个构造题,题目是【LGR-212-Div.2】洛谷 12 月月赛 II & Cfz Round 5 的 B 题(P11486),这道题是个比较简单的图论构造。Deepseek R1 在经过长达 1566 秒的思考(可能是使用高峰期,输出效率明显慢了很多,就当它慢了 67%,那也是 500 秒的思考),给出了一个依赖于度数的错误构造方式:
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<int> cnt(n + 1);
for (int i = 0; i < m; ++i) {
int s, t;
cin >> s >> t;
cnt[s]++;
cnt[t]++;
}
vector<int> odd_nodes;
for (int i = 1; i <= n; ++i) {
if (cnt[i] % 2 != 0) {
odd_nodes.push_back(i);
}
}
int k = odd_nodes.size();
if (k % 2 != 0 || k < 2) {
cout << "No\n";
return 0;
}
int c = -1;
for (int i = 1; i <= n; ++i) {
if (cnt[i] % 2 == 0) {
c = i;
break;
}
}
if (c == -1) {
cout << "No\n";
return 0;
}
vector<int> other_nodes;
for (int i = 1; i <= n; ++i) {
if (i != c && cnt[i] % 2 == 0) {
other_nodes.push_back(i);
}
}
if (other_nodes.empty() && k >= 2) {
cout << "No\n";
return 0;
}
vector<pair<int, int>> edges;
int m_node = (other_nodes.empty() ? c : other_nodes[0]);
int pairs = k / 2;
for (int i = 0; i < pairs - 1; ++i) {
int s = odd_nodes[2 * i];
int t = odd_nodes[2 * i + 1];
edges.emplace_back(s, c);
edges.emplace_back(t, c);
}
int remaining = k - 2 * (pairs - 1);
if (remaining == 2) {
int s = odd_nodes[k - 2];
int t = odd_nodes[k - 1];
edges.emplace_back(s, c);
if (!other_nodes.empty()) {
edges.emplace_back(c, m_node);
edges.emplace_back(m_node, t);
} else {
edges.emplace_back(c, t);
}
}
set<int> used;
for (auto &p : edges) {
used.insert(p.first);
used.insert(p.second);
}
for (int i = 1; i <= n; ++i) {
if (used.count(i)) continue;
if (edges.empty()) {
edges.emplace_back(i, c);
} else {
edges.emplace_back(i, edges.back().first);
}
}
if (edges.size() != n - 1) {
cout << "No\n";
return 0;
}
cout << "Yes\n";
for (auto &p : edges) {
cout << p.first << ' ' << p.second << '\n';
}
return 0;
}
综合上面的一些实践,我认为虽然 Deepseek R1 具有很强的文字工作能力,但是 Deepseek R1 很难具有 CF Rating 2000 左右的算法竞赛能力。也欢迎各位继续提供一些案例,来证明/证伪 Deepseek R1 具有相当于 CF 紫名的水平。