首页 > 其他分享 >树形DP + 换根DP

树形DP + 换根DP

时间:2023-07-28 10:24:30浏览次数:28  
标签:head idx int next 树形 节点 include 换根 DP

树形DP——基础

P1352 没有上司的舞会

设 \(f[i][0/1]\) 表示第 \(i\) 个人不去或者去。

如果第 \(i\) 个人没去,那么下属可去可不去,所以 \(f[i][0] = \sum max\{f[j][0],f[j][1]\}\),\(j\) 为 \(i\) 的子节点。

如果第 \(i\) 个人去了,那么下属不能去,所以 \(f[i][1] = a[i] + \sum f[j][0]\),\(j\) 为 \(i\) 的子节点。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 6010;

struct Edge {
	int to, next;
}e[N * 2];

int head[N], idx;

void add(int a, int b) {
	idx++, e[idx].to = b, e[idx].next = head[a], head[a] = idx;
}

int n;
int a[N];
int f[N][2];

void dfs(int u, int fa) {
	f[u][1] = a[u];
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		if (to == fa) continue;
		dfs(to, u);
		f[u][1] += f[to][0];
		f[u][0] += max(f[to][0], f[to][1]);
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = 1; i < n; i++) {
		int a, b;
		cin >> a >> b;
		add(a, b);
		add(b, a);
	}
	dfs(1, 0);
	cout << max(f[1][0], f[1][1]) << '\n';
	return 0;
}

POJ 1463 / UVA1292 Strategic game

与上一题类似,

设 \(f[i][0/1]\) 表示第 \(i\) 个人是否驻守。

如果第 \(i\) 个人没去,那么它的儿子必须驻守,所以 \(f[i][0] = 1 + \sum f[j][1]\),\(j\) 为 \(i\) 的子节点。

如果第 \(i\) 个人去了,那么它的儿子可去可不去,所以 \(f[i][1] = \sum min\{f[j][0],f[j][1]\}\),\(j\) 为 \(i\) 的子节点。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1510;

struct Edge {
	int to, next;
}e[N * 2];

int head[N], idx;

void add(int a, int b) {
	idx++, e[idx].to = b, e[idx].next = head[a], head[a] = idx;
}

int n;
int f[N][2];

void dfs(int u, int fa) {
	f[u][1] = 1;
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		if (to == fa) continue;
		dfs(to, u);
		f[u][0] += f[to][1];
		f[u][1] += min(f[to][0], f[to][1]);
	}
}

void solve() {
	memset(f, 0, sizeof(f));
	memset(head, 0, sizeof(head));
	idx = 0;
	for (int i = 1; i <= n; i++) {
		int a, num;
		scanf("%d:(%d)", &a, &num);
		a++;
		for (int j = 1; j <= num; j++) {
			int b;
			scanf("%d", &b);
			b++;
			add(a, b);
			add(b, a);
		}
	}
	dfs(1, 0);
	printf("%d\n", min(f[1][0], f[1][1]));
}

int main() {
	while (~scanf("%d", &n)) solve();
	return 0;
}

P3574 [POI2014] FAR-FarmCraft

设 \(f[i]\) 表示以 \(i\) 为根节点的子树中(包括节点 \(i\))的所有人安装好游戏所需要的时间(与下面的 \(g[i]\) 并没有包含关系,管理员也没有强制性要求要回到根节点,比如会出现下图情况)。

设 \(g[i]\) 表示从 \(i\) 开始往下走,兜一圈又回到 \(i\) 所需要的时间。

实际上 \(f[i]\) 可能 \(< g[i]\),比如当出现如下情况的时候:

image

那我们先访问那个节点呢?

分为两种情况考虑,即 \(f[i] - g[i] \geq 0\) 和 \(f[i] - g[i] < 0\) 两种情况。

如果管理员回到了起点那些人还没有装完(即 \(f[i] - g[i] \geq 0\)),那么就需要等待 \(f[i] - g[i]\) 的时间所有人才能安装好。

根据常识,在等待的这段时间我们可以去下一家,以减少所需的总时间。

这里我们利用贪心,让需要等待时间最久的作为第一个访问的节点,

这样可以管理员在他漫长的安装时间内将电脑送给其他人。

而如果出现了像上图一样的情况(即 \(f[i] - g[i] < 0\)) 的情况,

根本就不需要等待,

也就不用排序,

随机访问即可,

但为了简单起见,

排了序也没有什么问题。

所以我们可以对 \(f[i] - g[i]\) 从大到小进行排序。

再挨个访问即可。

然后就是利用 \(f\) 和 \(g\) 来用子树信息更新父亲节点。

如下图:

image

先说结论:只安装到 \(i\) 点会需要 \(\sum (g[j] + 2) + 1 + f[i]\) 的时间能完成安装,其中 \(j\) 为比 \(i\) 先遍历到的同一层的节点(如上图)。

为什么是这样呢?

第一部分的 \(\sum (g[j] + 2)\) 表示遍历完所有 \(j\) 子树的节点,每次都回到根节点(所以要 \(+2\))。

第二部分的 \(+1\) 表示从根节点走到 \(i\) 所需要的步骤(即为 \(1\) 步)。

最后一部分的 \(f[i]\) 表示把 \(i\) 子树内所有的游戏装好了需要花的时间。

总时间取 \(\max\) 即可, 即 \(f[root] = \max\{\sum (g[j] + 2) + f[i] + 1\}\)。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int N = 500010;

struct Edge {
	int to, next;
}e[N * 2];

int head[N], idx;

void add(int a, int b) {
	idx++;
	e[idx].to = b;
	e[idx].next = head[a];
	head[a] = idx;
}

int n, t[N];
int f[N], g[N];

void dfs(int u, int fa) {
	vector<int> wait;
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		if (to == fa) continue;
		dfs(to, u);
		wait.push_back(to);
	}
	sort(wait.begin(), wait.end(), [](const int& a, const int& b) { return f[a] - g[a] > f[b] - g[b]; });
	for (int i = 0; i < wait.size(); i++) {
		f[u] = max(f[u], g[u] + 1 + f[wait[i]]);
		g[u] += g[wait[i]] + 2;
	}
	if (t[u] > g[u] && u != 1) f[u] = max(f[u], t[u]);
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	cin >> n;
	for (int i = 1; i <= n; i++) cin >> t[i];
	for (int i = 1; i < n; i++) {
		int a, b;
		cin >> a >> b;
		add(a, b);
		add(b, a);
	}
	dfs(1, 0);
	cout << max(f[1], g[1] + t[1]) << '\n';
	return 0;
}

树形DP——树上背包

P2014 CTSC1997 选课

不难发现这是个树结构。

课程相当于树上的每一个节点,

学分相当于权值。

设 \(f[i][j][k]\) 表示遍历到第 \(i\) 棵子树的第 \(j\) 个元素并且选择 \(k\) 个节点可以得到的最大权值。

\(f[cur][i][j] = \max \{f[cur][i - 1][j - l] + f[ver[cur][i]][size[ver[i]]][l]\}\)。

其中 \(ver[i][j]\) 表示 \(i\) 节点的第 \(j\) 个子节点,\(size[x]\) 表示以 \(x\) 为根节点的子树的大小。

然后像01背包一样把第二维滚掉就可以了。

C++代码

版本1:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 310;

struct Edge {
	int to, next;
}e[N * 2];

int head[N], idx;

void add(int a, int b) {
	idx++;
	e[idx].to = b;
	e[idx].next = head[a];
	head[a] = idx;
}

int n, m;
int sz[N];
int a[N];
int f[N][N];

void dfs(int u) {
	sz[u] = 1;
	f[u][1] = a[u];
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		dfs(to);
		sz[u] += sz[to];
		for (int j = min(sz[u], m + 1); j >= 1; j--) {
			for (int k = 1; k <= min(sz[to], j - 1); k++) {
				f[u][j] = max(f[u][j], f[u][j - k] + f[to][k]);
			}
		}
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		int b;
		cin >> b >> a[i];
		add(b, i);
	}

	dfs(0);
	cout << f[0][m + 1] << '\n';
	return 0;
}

版本2:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 310;

struct Edge {
	int to, next;
}e[N * 2];

int head[N], idx;

void add(int a, int b) {
	idx++;
	e[idx].to = b;
	e[idx].next = head[a];
	head[a] = idx;
}

int n, m;
int sz[N];
int a[N];
int f[N][N];

void dfs(int u) {
	sz[u] = 1;
	f[u][1] = a[u];
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		dfs(to);
		for (int j = min(sz[u], m + 1); j >= 1; j--) {
			for (int k = 1; k <= sz[to] && j + k <= m + 1; k++) {
				f[u][j + k] = max(f[u][j + k], f[u][j] + f[to][k]);
			}
		}
		sz[u] += sz[to];
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		int b;
		cin >> b >> a[i];
		add(b, i);
	}

	dfs(0);
	cout << f[0][m + 1] << '\n';
	return 0;
}

P4516 [JSOI2018] 潜入行动

设 \(f[i][j][0/1][0/1]\) 表示在以 \(i\) 为根节点的子树中除节点 \(i\) 的全部节点被覆盖而且使用了 \(j\) 个监听设备时节点 \(i\) 的状况,第一个 \([0/1]\) 表示有没有放置监听设备,第二个 \([0/1]\) 表示 \(i\) 有没有被覆盖。

有:

\(f[u][i + j][0][0] = \sum f[u][i][0][0] \times f[v][j][0][1]\)

\(f[u][i + j][0][1] = \sum f[u][i][0][1] \times (f[v][j][0][1] + f[v][j][1][1]) + f[u][i][0][0] \times f[v][j][1][1]\)

\(f[u][i + j][1][0] = \sum f[u][i][1][0] \times (f[v][j][0][0] + f[v][j][0][1])\)

\(f[u][i + j][1][1] = \sum f[u][i][1][1] \times (f[v][j][0][0] + f[v][j][0][1] + f[v][j][1][0] + f[v][j][1][1]) + f[u][i][1][0] \times (f[v][j][1][0] + f[v][j][1][1])\)

注意:

1. 每次计算时都要把 \(f[u]\) 清空重新统计,所以只能用 \(tmp\) 数组当一下“替身 ”了。*

2. 此题卡空间、时间。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>

using namespace std;

const int N = 100010, M = 110, mod = 1000000007;

struct Edge {
	int to, next;
}e[N * 2];

int head[N], idx;

void add(int a, int b) {
	idx++;
	e[idx].to = b;
	e[idx].next = head[a];
	head[a] = idx;
}

struct num {
	int x;
	num operator+(num b) {
		while (b.x >= mod) b.x -= mod;
		long long tmp = 1ll * x + b.x;
		while (tmp >= mod) tmp -= mod;
		num res;
		res.x = tmp;
		return res;
	}

	void operator+=(num b) {
		while (b.x >= mod) b.x -= mod;
		long long tmp = 1ll * x + b.x;
		while (tmp >= mod) tmp -= mod;
		x = tmp;
	}

	num operator*(num b) {
		num res;
		res.x = (1ll * x * b.x) % mod;
		return res;
	}

	void operator=(int k) {
		x = k;
	}
};

int n, k;
int sz[N];
num f[N][M][2][2];
num t[M][2][2];

void dfs(int u, int fa) {
	sz[u] = 1;
	f[u][0][0][0] = 1;
	f[u][1][1][0] = 1;
	for (int tmp = head[u]; tmp; tmp = e[tmp].next) {
		int v = e[tmp].to;
		if (v == fa) continue;
		dfs(v, u);
		memcpy(t, f[u], sizeof(t));
		memset(f[u], 0, sizeof(f[u]));
		for (int i = min(sz[u], k); i >= 0; i--) {
			for (int j = 0; j <= min(sz[v], k - i); j++) {
				f[u][i + j][0][0] += t[i][0][0] * f[v][j][0][1];
				f[u][i + j][0][1] += t[i][0][1] * (f[v][j][0][1] + f[v][j][1][1]) + t[i][0][0] * f[v][j][1][1];
				f[u][i + j][1][0] += t[i][1][0] * (f[v][j][0][0] + f[v][j][0][1]);
				f[u][i + j][1][1] += t[i][1][1] * (f[v][j][0][0] + f[v][j][0][1] + f[v][j][1][0] + f[v][j][1][1]) + t[i][1][0] * (f[v][j][1][0] + f[v][j][1][1]);
			}
		}
		sz[u] += sz[v];
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	cin >> n >> k;
	for (int i = 1; i < n; i++) {
		int x, y;
		cin >> x >> y;
		add(x, y);
		add(y, x);
	}
	dfs(1, 0);
	cout << (f[1][k][0][1] + f[1][k][1][1]).x << '\n';
	return 0;
}

换根DP

换根法思路:

  1. 自下而上递推;
  2. 自上而下递推。

P3478 [POI2008] STA-Station

题目描述:

image

思路:

个节点 \(u\) 为根的子树大小 \(s[u]\)。

然后我们设 \(f[i]\) 为以 \(i\) 为根时所有节点的深度之和,\(j\) 为 \(i\) 的子节点。

那么对于所有 \(j\) 的子节点,深度都减 \(1\),所以总共减少了 \(s[j]\)。

对于所有不是 \(j\) 的子节点的节点,深度都加 \(1\) ,所以总共加了 \(n - s[j]\)。

所以 \(f[j] = f[i] - s[j] + n - s[j] = f[i] + n - 2 \times s[j]\)。

最后取 \(\max\) 即可。

代码:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
using i64 = long long;

const int N = 1000010;

struct Edge {
	int to, next;
}e[N * 2];

int head[N], idx;

void add(int a, int b) {
	idx++;
	e[idx].to = b;
	e[idx].next = head[a];
	head[a] = idx;
}

int n;
int sz[N];

void dfs1(int u, int fa) {
	sz[u] = 1;
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		if (to == fa) continue;
		dfs1(to, u);
		sz[u] += sz[to];
	}
}

int f[N];
int maxv, ans;

void dfs2(int u, int fa) {
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		if (to == fa) continue;
		f[to] = f[u] + n - 2 * sz[to];
		dfs2(to, u);
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	cin >> n;
	for (int i = 1; i < n; i++) {
		int x, y;
		cin >> x >> y;
		add(x, y);
		add(y, x);
	}
	dfs1(1, 0);
	for (int i = 1; i <= n; i++) f[1] += sz[i];
	dfs2(1, 0);
	for (int i = 1; i <= n; i++) if (f[i] > maxv) maxv = f[i], ans = i;
	cout << ans << '\n';
	return 0;
}

AcWing 287. 积蓄程度 / POJ 3585 Accumulation Degree

题目描述:

image

思路:

设 \(d[i]\) 表示 \(i\) 点可以向下传递的最大流量。

设 \(f[i]\) 表示 \(i\) 点可以向外传递的最大流量(包括向下流的流量和向上流的流量)。

先 \(\text{dfs}\) 一遍求出 \(d\)。

有:

如无特殊说明 \(to\) 表示 \(i\) 的子节点,\(edge(i, to)\) 表示 \(i\) 和 \(to\) 这条边的最大流量。

\(d[i] = \sum \min(d[to], edge(i, to))\)

\(f[to] = \min(edge(i, to), f[i] - \min(edge(i, to), d[to]))\)

同时注意如果根的度为 \(1\)(如下图) ,那么计算它的子节点时要采用公式:

\(f[i] = f[to] + edge(i, to)\)

image

代码:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 200010, INF = 0x3f3f3f3f;

struct Edge {
	int to;
	int next;
	int w;
}e[N * 2];

int head[N], idx, deg[N];

void add(int a, int b, int c) {
	idx++;
	e[idx].to = b;
	e[idx].next = head[a];
	e[idx].w = c;
	head[a] = idx;
}

int n;
int f[N], d[N];

int dfs1(int u, int fa) {
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		if (to == fa) continue;
		d[u] += min(dfs1(to, u), e[i].w);
	}
	if (deg[u] == 1) return INF;
	return d[u];
}

void dfs2(int u, int fa) {
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		if (to == fa) continue;
		if (deg[u] == 1) f[to] = d[to] + e[i].w;
		else f[to] = d[to] + min(e[i].w, f[u] - min(d[to], e[i].w));
		dfs2(to, u);
	}
}

void solve() {
	idx = 0;
	memset(head, 0, sizeof(head));
	memset(deg, 0, sizeof(deg));
	memset(f, 0, sizeof(f));
	memset(d, 0, sizeof(d));
	cin >> n;
	for (int i = 1; i < n; i++) {
		int x, y, z;
		cin >> x >> y >> z;
		add(x, y, z);
		add(y, x, z);
		deg[x]++;
		deg[y]++;
	}
	dfs1(1, 0);
	f[1] = d[1];
	dfs2(1, 0);
	int ans = 0;
	for (int i = 1; i <= n; i++) ans = max(ans, f[i]);
	cout << ans << '\n';
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int T;
	cin >> T;
	while (T--) solve();

	return 0;
}

P2986 [USACO10MAR] Great Cow Gathering G

题目描述:

image

思路:

设 \(f[u]\) 表示如果选择集会的地点为 \(u\) 点时所需时间。

根据经验,我们可以得到

\(f[to] = f[u] - to\text{节点下所有奶牛个数} \times w(u, to) + (奶牛总数 - to\text{节点下所有奶牛个数}) \times w(u, to)\)。

代码:

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int N = 100010, M = 200010;

struct Edge {
    int to;
    int next;
    int w;
}e[M];

int head[N], idx;

void add(int a, int b, int c) {
    idx++;
    e[idx].to = b;
    e[idx].next = head[a];
    e[idx].w = c;
    head[a] = idx;
}

int sz[N];
int cnt[N];

int dfs(int u, int fa) {
    int all = 0;
    sz[u] = cnt[u];
    for (int i = head[u]; i; i = e[i].next) {
        int to = e[i].to;
        if (to == fa) continue;
        all += dfs(to, u);
        sz[u] += sz[to];
        all += sz[to] * e[i].w;
    }
    return all;
}

int f[N], n, get_n;

void dfs2(int u, int fa) {
    for (int i = head[u]; i; i = e[i].next) {
        int to = e[i].to;
        if (to == fa) continue;
        f[to] = f[u] - sz[to] * e[i].w + (get_n - sz[to]) * e[i].w;
        dfs2(to, u);
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n;
    for (int i = 1; i <= n; i++) cin >> cnt[i], get_n += cnt[i];
    for (int i = 1; i < n; i++) {
        int x, y, z;
        cin >> x >> y >> z;
        add(x, y, z);
        add(y, x, z);
    }

    f[1] = dfs(1, 0);
    dfs2(1, 0);

    int min_sum = 0x3f3f3f3f3f3f3f3f;
    for (int i = 1; i <= n; i++) {
        if (min_sum > f[i]) {
            min_sum = f[i];
            // ans = i;
        }
    }
    cout << min_sum << '\n';
    return 0;
}

P3047 [USACO12FEB]Nearby Cows G

题目描述

image

思路

使用换根DP,

设 \(dp[i][j]\) 表示以 \(i\) 为根节点的子树中深度小于等于 \(j\) 的点的权值之和。

设 \(f[i][j]\) 表示将第 \(i\) 个点作为整棵树的根节点深度小于等于 \(j\) 的点的权值之和。

image

有:

\[\begin{cases} dp[u][k] = \sum dp[to][k - 1] \\ f[to][j] = dp[to][j] - dp[to][j - 2] + f[u][j - 1] \end{cases} \]


\(f[to][j] = dp[to][j] - dp[to][j - 2] + f[u][j - 1]\) 表示:

image

代码:

#include <bits/stdc++.h>

using namespace std;

const int N = 100010, M = 30;

int w[N];

struct Edge {
	int to;
	int next;
}e[N * 2];

int head[N], idx;

void add(int a, int b) {
	idx++;
	e[idx].to = b;
	e[idx].next = head[a];
	head[a] = idx;
}

int n, k;
int dp[N][M];
int f[N][M];

void dfs(int u, int fa) {
    for (int i = 0; i <= k; i++) dp[u][i] = w[u];
    for (int i = head[u]; i; i = e[i].next) {
        int to = e[i].to;
        if (to == fa) continue;
        dfs(to, u);
        for (int i = 1; i <= k; i++) dp[u][i] += dp[to][i - 1];
    }
}

void dfs2(int u, int fa) {
    for (int i = head[u]; i; i = e[i].next) {
        int to = e[i].to;
        if (to == fa) continue;
        f[to][1] = dp[to][1] + w[u];
        for (int j = k; j >= 2; j--) f[to][j] = f[u][j - 1] - dp[to][j - 2] + dp[to][j];
        dfs2(to, u);
    }
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
    cin >> n >> k;
    for (int i = 1; i < n; i++) {
        int a, b;
        cin >> a >> b;
        add(a, b);
        add(b, a);
    }
    for (int i = 1; i <= n; i++) cin >> w[i];
    dfs(1, 0);
    memcpy(f[1], dp[1], sizeof(f[1]));
    dfs2(1, 0);
    for (int i = 1; i <= n; i++) cout << f[i][k] << '\n';
	return 0;
}

标签:head,idx,int,next,树形,节点,include,换根,DP
From: https://www.cnblogs.com/PlayWithCPP/p/17586872.html

相关文章

  • t113-c-udp篇
    之前学习了tcp发送数据,但是效果很不理想,那么试一下用udp会怎么样,学习一下udp是怎么用用的吧看例子例子是在百问网下载的,serverclient这个有点奇怪,有点看不懂#include<sys/types.h>/*SeeNOTES*/#include<sys/socket.h>#include<string.h>#include<sys......
  • .Net6基于layui和ztree完成树形选择器添加和反填和修改
    以责任科室为例存储两个值ResponsibleDepartment和AoId,ResponsibleDepartment:是科室名称,AoId是科室Id添加:<divclass="layui-form-itemlayui-form-text"><labelclass="layui-form-label">责任科室</label><divclass="layui-inpu......
  • 暑假专题训练 dp 2023-7-26
    L.HamiltonianWall算法:dp做法:由于要一笔将所有黑块都划出来。所以我们状态转移方程应尽可能从左上角或者右下角的黑方块转移过来。$$f[i,j]=f[i,j-1]+1\(wall[i,j-1]=B,w[i,j]=B)$$$$f[i][j]=f[i+1][j-1]+2\(i==1,wall[i+1][j-1]==B,wal......
  • ThreadPoolExecutor源码分析
    packagejava.util.concurrent;importjava.util.concurrent.locks.*;importjava.util.*;publicclassThreadPoolExecutorextendsAbstractExecutorService{/***runStateprovidesthemainlifecylecontrol,takingonvalues:**......
  • wordpress 插件 woocommerce自定义订单信息验证
    使用php钩子函数增加自定义验证add_action('woocommerce_after_checkout_validation',function($fields){if($fields['billing_phone']&&!preg_match('/^((\+1|1)?(|-)?)?(\([2-9][0-9]{2}\)|[2-9][0-9]{2})(|-)?([2-9][0-9]{2}(|-)?[0-9......
  • ETHERNET/IP转PROFIBUS-DP网关EtherNet/IP与Profibus DP通讯网关
    大家好,今天要给大家介绍一款非常神奇的通讯网关捷米特JM-DPM-EIP!这款产品可以将各种PROFIBUS-DP从站接入到ETHERNET/IP网络中,真是一款神奇的产品啊!你是否想过,如果没有这款产品,PROFIBUS-DP从站和ETHERNET/IP网络之间该怎么通讯呢?让我们来看看这款产品到底有哪些神奇之处吧! 这款......
  • 替代LT8611芯片设计|CS5218设计方案|DP++转HDMI4K30HZ转换芯片方案
    ASL北京集睿致远研发CS5218DP转HDMI4K30HZ转换芯片,支持高达3840x2160@30Hz或者4096x2160@30Hz,主要用于设计TYEPC拓展坞和DP转接线的开发与应用。CS5218芯片设计电路:CS5218替代LT8611芯片包括2路双模DP电缆适配器寄存器,可用于识别电缆适配器的功能。可以更好地满足更高数......
  • java udp 广播地址
    实现JavaUDP广播地址简介在Java中,我们可以使用UDP协议进行网络通信。UDP是一种无连接的协议,它可以实现快速的数据传输,适用于实时性要求较高的场景。广播是一种UDP的应用场景,它可以将一条消息发送给同一网络中的所有设备。本文将指导刚入行的开发者实现JavaUDP广播地址的功能。......
  • 常见 DP 模型学习笔记
    一些经典的DP类型。I.数位DP数位DP归在此处,无论是高位往低位还是低位往高位。需要注意数位DP的本质是一种按位比较的贪心思想,因而可以加以扩展。I.[CQOI2013]二进制A+B最后判无解试了很多次才判成功……主要是因为“\(a,b,c\leq2^{30}\)中有个\(\leq\)而不是\(<\)就很......
  • TCP和UDP的区别以及各自的应用场景
    TCP和UDP区别:1.连接TCP是面向连接的传输层协议,传输数据前先要建立连接。UDP是不需要连接的,即刻就可以传输数据。2.服务对象TCP是一对一的两点服务,也就是说一条连接只有两个端点。UDP支持一对一,一对多,多对多的交互通信。3.可靠性TCP是要保证可靠交付数据的,数据无差错,不丢失......