OI 模版
OI 模板
1. 最长不下降子序列(LIS)
1.1 O(n^2) 做法
for (int i = 1;i <= n;i++) {
cin >> a[i];
dp[i] = 1;
}
for (int i = 1;i <= n;i++) {
for (int j = 1;j < i;j++) {
if (a[i] > a[j]) {
dp[i] = max(dp[i],dp[j] + 1);
}
}
ans = max(ans,dp[i]);
}
1.2 O(n log n) 做法
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int dp[N], n, x, k = 1;
int main() {
cin >> n >> x;
dp[1] = x;
for (int i = 2; i <= n; i++) {
scanf("%d",&x);
if (x > dp[k]) {
dp[++k] = x;
} else {
int p = lower_bound(dp + 1, dp + k + 1, x) - dp;
dp[p] = x;
}
}
cout << k;
return 0;
}
2. 背包问题
2.1 01背包-二维数组
cin >> maxw >> n;
for (int i = 1;i <= n;i++) {
cin >> w >> v;
for (int j = 1;j <= maxw;j++) {
if (w > j) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - w] + v);
}
}
}
cout << dp[n][maxw];
2.2 01背包-一维数组优化
cin >> maxv >> n; // maxv: 价值
for (int i = 1;i <= n;i++) {
cin >> v;
for (int j = maxv;j >= v;j--) {
dp[j] = max(dp[j],dp[j - v] + v);
}
}
cout << dp[maxv];
2.3 完全背包-一维数组优化
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e4 + 10, S = 1e7 + 10;
ll dp[S], vi, wi, maxw, n;
int main() {
scanf("%lld%lld", &maxw, &n);
for (int i = 1; i <= n; i++) {
scanf("%lld%lld", &wi, &vi);
for (int j = wi; j <= maxw; j++) {
dp[j] = max(dp[j], dp[j - wi] + vi);
}
}
printf("%lld", dp[maxw]);
return 0;
}
2.4 多重背包-普通版本
#include <iostream>
using namespace std;
const int N = 110;
int dp[N], v, s, w;
int n, maxw;
int main() {
scanf("%d%d", &n, &maxw);
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", &w, &v, &s);
for (int k = 1; k <= s; k++) {
for (int j = maxw; j >= w; j--) {
dp[j] = max(dp[j], dp[j - w] + v);
}
}
}
printf("%d", dp[maxw]);
return 0;
}
2.5 多重背包-二进制优化版
#include <iostream>
using namespace std;
const int N = 2e4 + 10;
int dp[2010], v[N], w[N];
int n, wi, vi, si, maxw, k;
int main() {
scanf("%d%d", &n, &maxw);
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", &wi, &vi, &si);
for (int t = 1; t <= si; t *= 2) {
k++;
v[k] = t * vi;
w[k] = t * wi;
si -= t;
}
if (si > 0) {
k++;
v[k] = si * vi;
w[k] = si * wi;
}
}
for (int i = 1; i <= k; i++) {
for (int j = maxw; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
printf("%d", dp[maxw]);
return 0;
}
2.6 分组背包
#include <iostream>
using namespace std;
const int N = 110;
int dp[N], v[N][N], w[N][N], s[N];
int n, maxw;
int main() {
scanf("%d%d", &n, &maxw);
for (int i = 1; i <= n; i++) {
scanf("%d", &s[i]);
for (int j = 1; j <= s[i]; j++) {
scanf("%d%d", &w[i][j], &v[i][j]);
}
}
for (int i = 1; i <= n; i++) {
for (int j = maxw; j >= 1; j--) {
for (int k = 1; k <= s[i]; k++) {
if (w[i][k] <= j)
dp[j] = max(dp[j], dp[j - w[i][k]] + v[i][k]);
}
}
}
printf("%d", dp[maxw]);
return 0;
}
2.7 二维费用背包
#include <iostream>
using namespace std;
const int N = 410;
int dp[N][N], maxw, maxv, vi, wi, c;
int n;
int main() {
scanf("%d%d", &maxw, &maxv);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> wi >> vi >> c;
for (int j = maxw; j >= wi; j--) {
for (int k = maxv; k >= vi; k--) {
dp[j][k] = max(dp[j][k], dp[j - wi][k - vi] + c);
}
}
}
cout << dp[maxw][maxv];
return 0;
}
3. 图论最短路
3.1 朴素 Dijkstra
时间复杂度:\(O(n^2)\)
#include <bits/stdc++.h>
using namespace std;
const int N = 60;
int a[N][N];
int d[N];
bool f[N];
int n,s;
int main() {
cin >> n >> s;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
cin >> a[i][j];
}
}
memset(d,0x3f,sizeof(d)); // 初始化为INF
d[s] = 0; // 走到起点的最短路为0
for (int i = 1;i <= n;i++) {
int mi = -1;
// 找到最小数下标
for (int j = 1;j <= n;j++) {
if (!f[j] && (mi == -1 || d[j] < d[mi])) {
mi = j;
}
}
f[mi] = true;
// 从最短路的点开始更新最短路
for (int j = 1;j <= n;j++) {
if (!f[j] && a[mi][j] && a[mi][j] + d[mi] < d[j]) {
d[j] = a[mi][j] + d[mi];
}
}
}
for (int i = 1;i <= n;i++) {
if (i != s) {
//走不到 -> 0x3f3f3f3f
if (d[i] == 0x3f3f3f3f) cout << -1 << " ";
else cout << d[i] << " ";
}
}
return 0;
}
3.2 优先队列 BFS
时间复杂度:\(O(m \times \log m)\)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
typedef pair<int,int> PII;
struct node{
int to,next,len;
}a[N];
int pre[N],k = 0;
int n,m,x,y,len;
priority_queue<PII,vector<PII>,greater<PII>> q; // 优先队列
void add(int x,int y,int len) { // 建边
a[++k] = {y,pre[x],len};
pre[x] = k;
}
bool f[N];
int d[N];
int main() {
scanf("%d%d",&n,&m);
for (int i = 1;i <= m;i++) {
scanf("%d%d%d",&x,&y,&len);
add(x,y,len);
}
// 初始化
q.push({0,1});
memset(d,0x3f,sizeof(d));
d[1] = 0;
while (!q.empty()) {
PII h = q.top();
int dis = h.first,p = h.second;
q.pop();
if (f[p]) continue;
f[p] = true;
if (p == n) { // 走到终点
cout << dis;
return 0;
}
for (int i = pre[p];i;i = a[i].next) { // 更新最短路
int to = a[i].to;
if (a[i].len + dis < d[to]) {
d[to] = a[i].len + dis;
q.push({a[i].len + dis,to});
}
}
}
cout << -1 << '\n';
return 0;
}
3.3 Floyd
时间复杂度:\(O(n^3)\)
#include <bits/stdc++.h>
using namespace std;
const int N = 110,INF = 0x3f3f3f3f;
int d[N][N];
int n,m,x,y,l;
int main() {
cin >> n >> m;
memset(d,0x3f,sizeof(d));
for (int i = 1;i <= n;i++) d[i][i] = 0;
for (int i = 1;i <= m;i++) {
cin >> x >> y >> l;
d[x][y] = l;
}
for (int k = 1;k <= n;k++) {
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
if (d[i][k] != INF && d[k][j] != INF) {
d[i][j] = min(d[i][j],d[i][k] + d[k][j]);
}
}
}
}
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
if (d[i][j] == INF) cout << "N ";
else cout << d[i][j] << " ";
}
cout << endl;
}
return 0;
}
3.4 SPFA
关于 SPFA,它死了。
点击查看代码
const int N = 2e3 + 10, INF = 0x3f3f3f3f;
struct node{
int to, next, len;
} a[N];
int pre[N], k, n, m, x, y, len, d[N];
queue<int> q;
bool f[N];
void add(int x, int y, int len) {
a[++k] = {y, pre[x], len};
pre[x] = k;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> x >> y >> len;
add(x, y, len);
}
memset(d, 0x3f, sizeof(d));
d[1] = 0;
q.push(1);
while (!q.empty()) {
int h = q.front();
q.pop();
f[h] = false;
for (int i = pre[h]; i; i = a[i].next) {
int to = a[i].to;
if (d[h] + a[i].len < d[to]) {
d[to] = d[h] + a[i].len;
if (!f[to]) {
f[to] = true;
q.push(to);
}
}
}
}
for (int i = 1; i <= n; i++) {
cout << (d[i] == INF ? -1 : d[i]) << ' ';
}
return 0;
}
4.树
4.1 树的直径
两次深搜求直径:
-
从树上任意找一个点 \(x\) 搜索离 \(x\) 最远的点 \(u\) ,\(u\) 一定是直径的一个端点;
-
从直径的一个端点 \(u\) 开始搜索,搜索出离 \(u\) 最远的点 \(v\) ,\(v\) 一定是直径的另一个端点,因此 \(uv\) 一定是直径。
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n,x,y;
struct node{
int to,next;
}a[N * 2];
int pre[N];
int k = 0;
int dep[N];
void add(int x,int y) {
a[++k] = {y,pre[x]};
pre[x] = k;
}
void dfs(int x,int fath) {
dep[x] = dep[fath] + 1;
for (int i = pre[x];i;i = a[i].next) {
if (a[i].to != fath) {
dfs(a[i].to,x);
}
}
}
int main() {
cin >> n;
for (int i = 1;i <= n - 1;i++) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
int x = 1;
// 求出离1号点最远的那个点
for (int i = 2;i <= n;i++) {
if (dep[i] > dep[x]) x = i;
}
dfs(x,0);
int y = 1;
// 找出离x号点最远的点y
for (int i = 1;i <= n;i++) {
if (dep[i] > dep[y]) y = i;
}
printf("%d",dep[y] - 1);
4.2 树的中心
树的中心指的是,该结点离树中的其他结点,最远距离最近。
树的中心满足如下性质:
(1)它一定在某条树的直径上,且到直径的两端的距离差不超过1。
(2)即使树有多条直径,但树的中心至多有2个,且均为直径的中点。
求树的中心:由于树的中心一定在直径上,求出直径的两个端点 \(u v\),加深 \(v\) 深度更深。
(1) 如果 \(dep[v]\) 是奇数,爬树 \(dep[v]/2\) 次;
(2) 如果 \(dep[v]\) 是偶数,爬树 \(dep[v]/2-1\) 次,得到两个中心的靠下的一个点,另一个点就是该点的父;
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n,x,y;
struct node{
int to,next;
}a[N * 2];
int pre[N];
int k = 0;
int dep[N],fa[N];
void add(int x,int y) {
a[++k] = {y,pre[x]};
pre[x] = k;
}
void dfs(int x,int fath) {
dep[x] = dep[fath] + 1;
fa[x] = fath;
for (int i = pre[x];i;i = a[i].next) {
if (a[i].to != fath) {
dfs(a[i].to,x);
}
}
}
int main() {
cin >> n;
for (int i = 1;i <= n - 1;i++) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
int x = 1;
for (int i = 2;i <= n;i++) {
if (dep[i] > dep[x]) x = i;
}
dfs(x,0);
int y = 1;
for (int i = 1;i <= n;i++) {
if (dep[i] > dep[y]) y = i;
}
if (dep[y] % 2 == 1) {
int d = dep[y] / 2;
for (int i = 1;i <= d;i++) {
y = fa[y];
}
printf("%d",y);
} else {
int d = dep[y] / 2 - 1;
for (int i = 1;i <= d;i++) {
y = fa[y];
}
if (y < fa[y]) cout << y << " " << fa[y];
else cout << fa[y] << " " << y;
}
return 0;
}
4.3 树的公共祖先(LCA)
解法一:染色法
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n,x,y;
struct node{
int to,next;
}a[N * 2];
int pre[N];
int k = 0;
int dep[N],fa[N];
void add(int x,int y) {
a[++k] = {y,pre[x]};
pre[x] = k;
}
bool vis[N];
void dfs(int x,int fath) {
dep[x] = dep[fath] + 1;
fa[x] = fath;
for (int i = pre[x];i;i = a[i].next) {
if (a[i].to != fath) {
dfs(a[i].to,x);
}
}
}
int main() {
cin >> n;
int u,v;
cin >> u >> v;
for (int i = 1;i <= n - 1;i++) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
vis[u] = true;
while (fa[u] != 0) {
u = fa[u];
vis[u] = true;
}
while (!vis[v]) {
v = fa[v];
}
cout << v;
return 0;
}
解法二:两个结点同时向上移动
思路:
(1)求出每个结点的深度;
(2)如果两个结点重叠,那么得知公共祖先;
(3)如果不重叠,选择深度更大的结点,向父元素移动,重复上述过程,直到重叠。
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int n,x,y;
struct node{
int to,next;
}a[N * 2];
int pre[N];
int k = 0;
int dep[N],fa[N];
void add(int x,int y) {
a[++k] = {y,pre[x]};
pre[x] = k;
}
bool vis[N];
void dfs(int x,int fath) {
dep[x] = dep[fath] + 1;
fa[x] = fath;
for (int i = pre[x];i;i = a[i].next) {
if (a[i].to != fath) {
dfs(a[i].to,x);
}
}
}
int main() {
cin >> n;
int u,v;
cin >> u >> v;
for (int i = 1;i <= n - 1;i++) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
while (u != v) {
if (dep[u] > dep[v]) {
u = fa[u];
} else {
v = fa[v];
}
}
cout << u;
return 0;
4.4 树的重心
求出每个结点删除后的最大子树的大小,并求出该值的最小值,最后循环看哪些树删除后子树大小==所求的最小值,哪些结点就是重心。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n,x,y;
struct node{
int to,next;
}a[N * 2];
int pre[N];
int k = 0;
int dep[N],fa[N],si[N];
int ma[N];
int mi = INT_MAX;
void add(int x,int y) {
a[++k] = {y,pre[x]};
pre[x] = k;
}
bool vis[N];
int dfs(int x,int fath) {
dep[x] = dep[fath] + 1;
fa[x] = fath;
si[x] = 1;
for (int i = pre[x];i;i = a[i].next) {
if (a[i].to != fath) {
si[x] += dfs(a[i].to,x);
}
}
return si[x];
}
int main() {
cin >> n;
for (int i = 1;i <= n - 1;i++) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
for (int i = 1;i <= n;i++) {
ma[i] = n - si[i];
for (int j = pre[i];j;j = a[j].next) {
if (a[j].to != fa[i]) {
ma[i] = max(ma[i],si[a[j].to]);
}
}
mi = min(ma[i],mi);
}
for (int i = 1;i <= n;i++) {
if (ma[i] == mi) {
cout << i << " ";
}
}
return 0;
}
5.最长公共子序列(LCS)
5.1 O(n^2) 做法
#include <iostream>
using namespace std;
const int N = 1010;
int n, dp[N][N], a[N], b[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (a[i] == b[j]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
printf("%d", dp[n][n]);
return 0;
}
5.2 O(n log n) 做法
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int dp[N], a[N], b[N], n, c[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
c[a[i]] = i;
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
dp[1] = c[b[1]];
int k = 1;
for (int i = 2; i <= n; i++) {
if (c[b[i]] > dp[k]) {
dp[++k] = c[b[i]];
} else {
int p = lower_bound(dp + 1, dp + k + 1, c[b[i]]) - dp;
dp[p] = c[b[i]];
}
}
printf("%d", k);
return 0;
}
6. 并查集
6.1 朴素并查集
int find(int x) {
if (x == fa[x]) return x;
return find(fa[x]);
}
void merge(int x, int y) {
int rx = find(x), ry = find(y);
if (rx != ry) fa[rx] = ry;
}
6.2 路径压缩
int find(int x) {
if (x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
7. 最小生成树
7.1 Kustral
for (int i = 1;i <= k;i++) {
if (find(a[i].x) != find(a[i].y)) {
merge(a[i].x, a[i].y);
sum += a[i].len;
c++;
if (c == n - 1) {
printf("%d", sum);
return 0;
}
}
}
8. 素数筛法
8.1 埃氏筛法
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
bool f[N];
int n, ans;
int main() {
scanf("%d", &n);
f[0] = f[1] = true;
for (int i = 2;i <= sqrt(n);i++) {
if (!f[i]) {
for (int j = i * 2;j <= n;j += i) {
if (!f[j]) {
f[j] = true;
}
}
}
}
for (int i = 1; i <= n; i++) {
if (!f[i]) ans++;
}
printf("%d", ans);
return 0;
}
8.2 欧拉筛法
f[0] = f[1] = true;
for (int i = 2;i <= e;i++) {
if (!f[i]) primes[++k] = i;
for (int j = 1;i * primes[j] <= e;j++) {
f[i * primes[j]] = true;
if (i % primes[j] == 0) break;
}
}
9. 字符串哈希
void gethash() {
p[0] = 1;
for (int i = 1; s[i]; i++) {
h[i] = h[i - 1] * P + (s[i] - 'a' + 1);
p[i] = p[i - 1] * P;
}
}
ull get(int l, int r) {
return h[r] - h[l - 1] * p[r - l + 1];
}
10. 哈希表
注意事项:数组要看看是否开够了对应的大小、别忘了有些变量需要 unsigned long long。
10.1 哈希表板子
void add(int x) {
int v = x % MOD;
e[++k] = x;
nxt[k] = h[v];
h[v] = k;
}
bool find(int x) {
int v = x % MOD;
for (int i = h[v]; i; i = nxt[i]) {
if (e[i] == x) return true;
}
return false;
}
10.2 哈希表 + 统计数量
void insert(ull x) {
int v = x % MOD;
for (int i = h[v]; i; i = nxt[i]) {
if (e[i] == x) {
cnt[i]++;
return;
}
}
e[++k] = x; nxt[k] = h[v]; h[v] = k; cnt[k] = 1;
}
int find(ull x) {
int v = x % MOD;
for (int i = h[v]; i; i = nxt[i]) {
if (e[i] == x) return cnt[i];
}
return 0;
}
11. 树状数组
11.1 lowbit
int lowbit(int x) { return x & -x; }
11.2 add(modify, update)
void add(int x, int y) {
for (; x <= n; x += lowbit(x)) tr[x] += y;
}
11.3 query
int query(int x) {
int s = 0;
for (; x > 0; x -= lowbit(x)) s += tr[x];
return s;
12. 线段树
void pushup(int p) {
// 利用子节点更新父节点
}
void build(int p, int l, int r) { // 建树
if (l == r) return tr[p] = a[l], void();
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
void change(int p, int l, int r, int v) { // 修改某个节点的值
tr[p] += 1LL * (r - l + 1) * v;
add[p] += v;
}
void pushdown(int p, int l, int r) { // 懒标记下放
if (add[p]) {
int mid = (l + r) >> 1;
change(p << 1, l, mid, add[p]);
change(p << 1 | 1, mid + 1, r, add[p]);
add[p] = 0;
}
}
void update(int p, int l, int r, int x, int y, int v) { // 更新
if (l >= x && r <= y) return change(p, l, r, v), void();
int mid = (l + r) >> 1;
pushdown(p, l, r);
if (x <= mid) update(p << 1, l, mid, x, y, v);
if (y > mid) update(p << 1 | 1, mid + 1, r, x, y, v);
pushup(p);
}
int query(int p, int l, int r, int x, int y) {
if (l >= x && r <= y) return tr[p];
int mid = (l + r) >> 1;
pushdown(p, l, r);
ll ans = 0;
if (x <= mid) ans = query(p << 1, l, mid, x, y);
if (y > mid) ans += query(p << 1 | 1, mid + 1, r, x, y);
/*
如果是取 max / min:
if (x <= mid) ans = query(p << 1, l, mid, x, y);
if (y > mid) ans = min/max (ans, query(p << 1 | 1, mid + 1, r, x, y));
*/
return ans;
}
13. LCA
13.1 倍增求 LCA
void dfs(int x, int fath) {
dep[x] = dep[fath] + 1;
f[x][0] = fath;
for (int i = pre[x]; i; i = a[i].next) {
int to = a[i].to;
if (to != fath) dfs(to, x);
}
}
int query(int x, int y) {
int l, u = x, v = y;
if (dep[x] < dep[y]) swap(x, y);
while (dep[x] != dep[y]) x = f[x][lg[dep[x] - dep[y]]];
if (x == y) l = x;
else {
for (int j = K - 1; j >= 0; j--) {
if (f[x][j] != f[y][j]) x = f[x][j], y = f[y][j];
}
l = f[x][0];
}
return (dep[u] - dep[l] + dep[v] - dep[l]);
}
13.2 Tarjan 求 LCA
还没学过,先鸽着。
14. Tarjan 取强连通分量(SCC)
void tarjan(int x) {
dfn[x] = low[x] = ++times;
inst[x] = true;
st.push(x);
for (int i = pre[x]; i; i = a[i].next) {
int to = a[i].to;
if (!dfn[to]) {
tarjan(to);
low[x] = min(low[x], low[to]);
} else if (inst[to]) low[x] = min(low[x], dfn[to]);
}
if (low[x] == dfn[x]) {
cnt++;
int t = 0;
do {
t = st.top();
st.pop();
si[cnt]++;
inst[t] = false;
id[t] = cnt;
} while (t != x);
}
}
15. Tarjan 缩点
void tarjan(int x) { // 和上文一样
...
}
int main() {
...
for (int i = 1; i <= m; i++) {
x = id[a[i].from], y = id[a[i].to];
if (x != y) { add(x, y, pre[2]); deg[y]++; } // 重新建图
}
}
16. 线段树
16.1 单点修改 + 区间查询
void update(int p, int l, int r, int x, int v) {
if (l == x && r == x) return tr[p] += v, void();
int mid = (l + r) >> 1;
if (x <= mid) update(p << 1, l, mid, x, v);
else update(p << 1 | 1, mid + 1, r, x, v);
tr[p] = tr[p << 1] + tr[p << 1 | 1];
}
int query(int p, int l, int r, int x, int y) {
if (l >= x && r <= y) return tr[p];
int mid = (l + r) >> 1, res = 0;
if (x <= mid) res = query(p << 1, l, mid, x, y);
if (y > mid) res += query(p << 1 | 1, mid + 1, r, x, y);
return res;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现