codeforces:Roads in the Kingdom分析和实现
题目大意:国家有n个城市,还有n条道路,每条道路连通两个不同的城市,n条道路使得所有n个城市相互连通。现在国家经费不足,要关闭一条道路。国家的不便度定义为国家中任意两个不同的城市之间的距离的最大值,那么求选择关闭哪一条道路能使得国家的不便度最小。
首先说明一下将城市看作图中的顶点,而道路则是连接各个顶点的边,因此整个国家可以看作一副图。并且由提供的信息知道这是一副连通图,而n顶点n边的连通图必然是存在环路的,只有移除环路上的边才能使得图保持连通,详情参阅我的另外一篇博客《连通图的一些性质》。
显然最优结果是移除环路上的边,因为移除其它边只会破坏图的连通性质,从而使得国家的不便度达到无穷(存在不连通城市)。那么具体该选择哪条边呢?
可以将整个图理解为一个环路,每个环路上的顶点上挂坠一个子连通图,不同顶点挂坠必须通过环路才能连通的不同子连通图,被挂坠的顶点属于挂坠在其上的子连通图,称这若干副连通图为孤立连通图,若其挂坠在环路上的结点V上,则称为V代表的孤立连通图。在这种情况下,最远的两个结点必然来自两幅不同或相同的孤立连通图。
由于从环中移除边,不会影响来自同一孤立连通图的顶点之间的距离,因此每个孤立连通图之间的最远的两个顶点可以率先计算出来。这个过程可以通过《连通图的一些性质》中命题4部分在O(n)时间复杂度内完成。
而对于不同孤立连通图的顶点,需要先计算每个环上结点V,与V代表的孤立连通图中顶点的最远距离,记作V.END,这个操作可以合并到上一步中,并且不会影响上一步的时间复杂度。而对于任意两个环上结点V,U,来自两个结点所代表孤立连通图的结点的距离最大值应该为WD(V,U)=V.END+U.END+V与U的距离D(V,U)。
将环写作以下顶点的序列V[1], V[2], ... , V[k],并假设其沿着顺时针(或逆时针)进行排序。记L(v, u)表示连接v和u的边的长度,接下来我们记E[i]=L(V[i], V[i+1]),其中i=1,..., k - 1,而E[k] = L(V[k], V[1])。这些操作在O(n)的时间可以完成。
记LEFT[i]表示在移除边(V[i], V[i+1])的前提下,V[1], ... , V[i]表示所代表的孤立连通图中最远的两个顶点的距离;而RIGHT[i]表示在移除边(V[i], V[i+1])的前提下,V[i+1], ... , V[k]所代表的孤立连通图中最远的两个顶点的距离;记CROSS[i]表示在移除边(V[i], V[i+1])的前提下,V[i+1], ... , V[k]所代表的孤立连通图中某个顶点到V[1], ... , V[i]所代表的孤立连通图中另外一个顶点之间最大的距离。因此当移除边(V[i], V[i+1])时,对于距离最远的来自不同的孤立连通图的两个顶点U, V,必然有
$$ WD\left(V,U\right)=MAX\left(LEFT\left[i\right],RIGHT\left[i\right],CROSS\left[i\right]\right) $$在已知LEFT,RIGHT,CROSS的情况下这部分总的时间复杂度为O(1)*n=O(n)。
说一下如果利用动态规划在O(n)时间复杂度内计算LEFT,RIGHT和CROSS。记每个顶点V[i]都维护了属性V[i].S=V[1].S+V[2].S+...V[i-1].S。维护数组W[i]=V[i].END+V.S,M[i]=V[i].END-V[i].S,LW[i]=MAX(W[1], W[2], ... , W[i-1]),LM[i]=MAX(M[1], M[2], ... ,M[i-1]), RW[i] = MAX(W[i+1], W[i+2], ... , W[k]),RM[i] = MAX(M[i+1], M[i+2], ... , M[k]),这可以在O(n)的时间复杂度内完成。
在移除边(V[i], V[i+1])的前提下,LEFT[i]值对应的两个顶点来自环上的顶点A,B所代表的孤立连通图,A,B中或者一者为V[i],或者二者均不是V[i]。在后面的情况下LEFT[i]=LEFT[i-1]。而在前面的情况下不妨设B=V[i],则WD(A,V[i])=A.END+V[i].END+D(A,V[i])=(A.END-A.S)+(V[i].END+V[i].S)=LM[i] + W[i]。即LEFT[i]=MAX(LEFT[i-1], LM[i] + W[i])
同样RIGHT[i]或者为RIGHT[i+1],或者为WD(V[i+1], B)=(V[i+1].END-V[i+1].S) + (B.END + B.S)=M[i+1] + RW[i+1]。即RIGHT[i]=MAX(RIGHT[i+1], M[i+1] + RW[i+1])。
而CROSS[i]=WD(A,B)=A.END+B.END+D(A,B)=A.END+B.END+(V[k].S+E[k]+B.S-A.S)=(B.S+B.END)+(A.END-A.S)+E[k]+V[k].S=RM[i]+LW[i+1]+E[k]+V[k].S。
因此整个DP的过程都可以在O(n)的时间复杂度内完成。
最后提供AC的JAVA代码:
package cn.dalt.codeforces;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* Created by dalt on 2017/9/6.
*/
public class RoadsInTheKingdom2 {
private static final long INFINITE_SMALL = (long) (-1e14);
int n;
Node[] allNodes;
LinkedList<Node> loop = new LinkedList<>();
LinkedList<Long> distanceToNextNode = new LinkedList<>();
public static void main(String[] args) throws Exception {
RoadsInTheKingdom2 instance = new RoadsInTheKingdom2();
instance.init(new AcmInputReader(System.in));
long result = instance.solve();
System.out.println(result);
}
void init(AcmInputReader input) throws Exception {
n = input.nextInteger();
allNodes = new Node[n];
for (int i = 0; i < n; i++) {
allNodes[i] = new Node();
allNodes[i].id = i + 1;
}
for (int i = 0; i < n; i++) {
int ui = input.nextInteger() - 1;
int vi = input.nextInteger() - 1;
int li = input.nextInteger();
allNodes[ui].addNearNode(allNodes[vi], li);
allNodes[vi].addNearNode(allNodes[ui], li);
}
}
boolean searchLoop(Node node, Node from)
{
if (node.flag == Node.FLAG_VISITED) {
//find a loop, and (from, node) is an edge in loop
//Remove (from, node)
long distance = node.removeNearNode(from);
from.removeNearNode(node);
searchLoop0(node, from, null);
loop.add(node);
distanceToNextNode.add(distance);
node.flag = Node.FLAG_IN_LOOP;
return true;
}
node.flag = Node.FLAG_VISITED;
for (Node next : node.nearNodes) {
if (next == from) {
continue;
}
if(searchLoop(next, node))
{
return true;
}
}
return false;
}
boolean searchLoop0(Node node, Node target, Node from) {
if (node == target) {
return true;
}
Iterator<Long> distanceIterator = node.distanceTo.iterator();
for (Node next : node.nearNodes) {
Long distanceTo = distanceIterator.next();
if (next == from) {
continue;
}
if (searchLoop0(next, target, node)) {
loop.add(next);
distanceToNextNode.add(distanceTo);
next.flag = Node.FLAG_IN_LOOP;
return true;
}
}
return false;
}
void preCalculateEndAndMid(Node node, Node from) {
Iterator<Long> distanceIterator = node.distanceTo.iterator();
SimpleHeap heap = new SimpleHeap();
long maxMid = 0;
for (Node next : node.nearNodes) {
Long distanceTo = distanceIterator.next();
if (next.flag == Node.FLAG_IN_LOOP) {
continue;
}
if (next == from) {
continue;
}
preCalculateEndAndMid(next, node);
heap.update(distanceTo + next.end);
maxMid = Math.max(maxMid, next.mid);
}
node.end = heap.max;
node.mid = Math.max(heap.max + heap.secondaryMax, maxMid);
}
void preCalculateAloneRelatedGraph() {
for (Node node : loop) {
preCalculateEndAndMid(node, null);
}
}
long solve() {
searchLoop(allNodes[0], null);
preCalculateAloneRelatedGraph();
long possibleResult = 0;
for (Node node : loop) {
possibleResult = possibleResult > node.mid ? possibleResult : node.mid;
}
//DP part
int loopSize = loop.size();
long[] cross = new long[loopSize];
long[] left = new long[loopSize];
long[] right = new long[loopSize];
{
long[] w = new long[loopSize];
long[] m = new long[loopSize];
long[] lw = new long[loopSize];
long[] lm = new long[loopSize];
long[] rw = new long[loopSize];
long[] rm = new long[loopSize];
long distanceToFirstVertexInLoop = 0;
{
int i = 0;
Iterator<Long> distanceIterator = distanceToNextNode.iterator();
for (Node node : loop) {
w[i] = node.end + distanceToFirstVertexInLoop;
m[i] = node.end - distanceToFirstVertexInLoop;
distanceToFirstVertexInLoop += distanceIterator.next();
i++;
}
}
lw[0] = INFINITE_SMALL;
lm[0] = INFINITE_SMALL;
left[0] = INFINITE_SMALL;
for (int i = 1; i < loopSize; i++) {
lw[i] = Math.max(lw[i - 1], w[i - 1]);
lm[i] = Math.max(lm[i - 1], m[i - 1]);
left[i] = Math.max(left[i - 1], lm[i] + w[i]);
}
rw[loopSize - 1] = INFINITE_SMALL;
rm[loopSize - 1] = INFINITE_SMALL;
right[loopSize - 1] = INFINITE_SMALL;
for (int i = loopSize - 2; i >= 0; i--) {
rw[i] = Math.max(rw[i + 1], w[i + 1]);
rm[i] = Math.max(rm[i + 1], m[i + 1]);
right[i] = Math.max(right[i + 1], rw[i + 1] + m[i + 1]);
}
for (int i = 0, bound = loopSize - 1; i < bound; i++) {
cross[i] = rm[i] + lw[i + 1] + distanceToFirstVertexInLoop;
}
}
//Find the best solution with DP results
long minInconvenienceCrossLoop = left[loopSize - 1];
for (int i = 0, bound = loopSize - 1; i < bound; i++) {
long inconvenience = 0;
inconvenience = Math.max(left[i], inconvenience);
inconvenience = Math.max(right[i], inconvenience);
inconvenience = Math.max(cross[i], inconvenience);
minInconvenienceCrossLoop = Math.min(minInconvenienceCrossLoop, inconvenience);
}
return Math.max(minInconvenienceCrossLoop, possibleResult);
}
private static class SimpleHeap {
long max = 0;
long secondaryMax = 0;
void update(long value) {
if (value > max) {
secondaryMax = max;
max = value;
} else if (value > secondaryMax) {
secondaryMax = value;
}
}
}
private static class Node {
public static final int FLAG_VISITED = 1;
public static final int FLAG_IN_LOOP = 2;
List<Node> nearNodes = new LinkedList<>();
List<Long> distanceTo = new LinkedList<>();
long end;
long mid;
int flag;
int id;
@Override
public String toString() {
return "" + id;
}
void addNearNode(Node node, long distance) {
nearNodes.add(node);
distanceTo.add(distance);
}
long distanceTo(Node node) {
Iterator<Node> nodeIterator = nearNodes.iterator();
Iterator<Long> distanceIteartor = distanceTo.iterator();
while (nodeIterator.next() != node) {
distanceIteartor.next();
}
return distanceIteartor.next();
}
long removeNearNode(Node node) {
Iterator<Node> nodeIterator = nearNodes.iterator();
Iterator<Long> distanceIteartor = distanceTo.iterator();
while (nodeIterator.next() != node) {
distanceIteartor.next();
}
nodeIterator.remove();
long result = distanceIteartor.next();
distanceIteartor.remove();
return result;
}
}
/**
* @author dalt
* @see java.lang.AutoCloseable
* @since java1.7
*/
private static class AcmInputReader implements AutoCloseable {
private PushbackInputStream in;
/**
* 创建读取器
*
* @param input 输入流
*/
public AcmInputReader(InputStream input) {
in = new PushbackInputStream(new BufferedInputStream(input));
}
@Override
public void close() throws IOException {
in.close();
}
private int nextByte() throws IOException {
return in.read() & 0xff;
}
/**
* 如果下一个字节为b,则跳过该字节
*
* @param b 被跳过的字节值
* @throws IOException if 输入流读取错误
*/
public void skipByte(int b) throws IOException {
int c;
if ((c = nextByte()) != b) {
in.unread(c);
}
}
/**
* 如果后续k个字节均为b,则跳过k个字节。这里{@literal k<times}
*
* @param b 被跳过的字节值
* @param times 跳过次数,-1表示无穷
* @throws IOException if 输入流读取错误
*/
public void skipByte(int b, int times) throws IOException {
int c;
while ((c = nextByte()) == b && times > 0) {
times--;
}
if (c != b) {
in.unread(c);
}
}
/**
* 类似于{@link #skipByte(int, int)}, 但是会跳过中间出现的空白字符。
*
* @param b 被跳过的字节值
* @param times 跳过次数,-1表示无穷
* @throws IOException if 输入流读取错误
*/
public void skipBlankAndByte(int b, int times) throws IOException {
int c;
skipBlank();
while ((c = nextByte()) == b && times > 0) {
times--;
skipBlank();
}
if (c != b) {
in.unread(c);
}
}
/**
* 读取下一块不含空白字符的字符块
*
* @return 下一块不含空白字符的字符块
* @throws IOException if 输入流读取错误
*/
public String nextBlock() throws IOException {
skipBlank();
StringBuilder sb = new StringBuilder();
int c = nextByte();
while (AsciiMarksLazyHolder.asciiMarks[c = nextByte()] != AsciiMarksLazyHolder.BLANK_MARK) {
sb.append((char) c);
}
in.unread(c);
return sb.toString();
}
/**
* 跳过输入流中后续空白字符
*
* @throws IOException if 输入流读取错误
*/
private void skipBlank() throws IOException {
int c;
while ((c = nextByte()) <= 32) ;
in.unread(c);
}
/**
* 读取下一个整数(可正可负),这里没有对溢出做判断
*
* @return 下一个整数值
* @throws IOException if 输入流读取错误
*/
public int nextInteger() throws IOException {
skipBlank();
int value = 0;
boolean positive = true;
int c = nextByte();
if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
positive = c == '+';
} else {
value = '0' - c;
}
c = nextByte();
while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
value = (value << 3) + (value << 1) + '0' - c;
c = nextByte();
}
in.unread(c);
return positive ? -value : value;
}
/**
* 判断是否到了文件结尾
*
* @return true如果到了文件结尾,否则false
* @throws IOException if 输入流读取错误
*/
public boolean isMeetEOF() throws IOException {
int c = nextByte();
if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.EOF) {
return true;
}
in.unread(c);
return false;
}
/**
* 判断是否在跳过空白字符后抵达文件结尾
*
* @return true如果到了文件结尾,否则false
* @throws IOException if 输入流读取错误
*/
public boolean isMeetBlankAndEOF() throws IOException {
skipBlank();
int c = nextByte();
if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.EOF) {
return true;
}
in.unread(c);
return false;
}
/**
* 获取下一个用英文字母组成的单词
*
* @return 下一个用英文字母组成的单词
*/
public String nextWord() throws IOException {
StringBuilder sb = new StringBuilder(16);
skipBlank();
int c;
while ((AsciiMarksLazyHolder.asciiMarks[(c = nextByte())] & AsciiMarksLazyHolder.LETTER_MARK) != 0) {
sb.append((char) c);
}
in.unread(c);
return sb.toString();
}
/**
* 读取下一个长整数(可正可负),这里没有对溢出做判断
*
* @return 下一个长整数值
* @throws IOException if 输入流读取错误
*/
public long nextLong() throws IOException {
skipBlank();
long value = 0;
boolean positive = true;
int c = nextByte();
if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
positive = c == '+';
} else {
value = '0' - c;
}
c = nextByte();
while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
value = (value << 3) + (value << 1) + '0' - c;
c = nextByte();
}
in.unread(c);
return positive ? -value : value;
}
/**
* 读取下一个浮点数(可正可负),浮点数是近似值
*
* @return 下一个浮点数值
* @throws IOException if 输入流读取错误
*/
public float nextFloat() throws IOException {
return (float) nextDouble();
}
/**
* 读取下一个浮点数(可正可负),浮点数是近似值
*
* @return 下一个浮点数值
* @throws IOException if 输入流读取错误
*/
public double nextDouble() throws IOException {
skipBlank();
double value = 0;
boolean positive = true;
int c = nextByte();
if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
positive = c == '+';
} else {
value = c - '0';
}
c = nextByte();
while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
value = value * 10.0 + c - '0';
c = nextByte();
}
if (c == '.') {
double littlePart = 0;
double base = 1;
c = nextByte();
while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
littlePart = littlePart * 10.0 + c - '0';
base *= 10.0;
c = nextByte();
}
value += littlePart / base;
}
in.unread(c);
return positive ? value : -value;
}
/**
* 读取下一个高精度数值
*
* @return 下一个高精度数值
* @throws IOException if 输入流读取错误
*/
public BigDecimal nextDecimal() throws IOException {
skipBlank();
StringBuilder sb = new StringBuilder();
sb.append((char) nextByte());
int c = nextByte();
while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
sb.append((char) c);
c = nextByte();
}
if (c == '.') {
sb.append('.');
c = nextByte();
while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
sb.append((char) c);
c = nextByte();
}
}
in.unread(c);
return new BigDecimal(sb.toString());
}
/**
* 读取下一个大整数数值
*
* @return 下一个大整数数值
* @throws IOException if 输入流读取错误
*/
public BigInteger nextBigInteger() throws IOException {
skipBlank();
StringBuilder sb = new StringBuilder();
sb.append((char) nextByte());
int c = nextByte();
while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
sb.append((char) c);
c = nextByte();
}
in.unread(c);
return new BigInteger(sb.toString());
}
private static class AsciiMarksLazyHolder {
public static final byte BLANK_MARK = 1;
public static final byte SIGN_MARK = 1 << 1;
public static final byte NUMERAL_MARK = 1 << 2;
public static final byte UPPERCASE_LETTER_MARK = 1 << 3;
public static final byte LOWERCASE_LETTER_MARK = 1 << 4;
public static final byte LETTER_MARK = UPPERCASE_LETTER_MARK | LOWERCASE_LETTER_MARK;
public static final byte EOF = 1 << 5;
public static byte[] asciiMarks = new byte[256];
static {
for (int i = 0; i <= 32; i++) {
asciiMarks[i] = BLANK_MARK;
}
asciiMarks['+'] = SIGN_MARK;
asciiMarks['-'] = SIGN_MARK;
for (int i = '0'; i <= '9'; i++) {
asciiMarks[i] = NUMERAL_MARK;
}
for (int i = 'a'; i <= 'z'; i++) {
asciiMarks[i] = LOWERCASE_LETTER_MARK;
}
for (int i = 'A'; i <= 'Z'; i++) {
asciiMarks[i] = UPPERCASE_LETTER_MARK;
}
asciiMarks[0xff] = EOF;
}
}
}
}
codeforces:Roads in the Kingdom分析和实现的更多相关文章
- Codeforces 835 F. Roads in the Kingdom
\(>Codeforces\space835 F. Roads in the Kingdom<\) 题目大意 : 给你一棵 \(n\) 个点构成的树基环树,你需要删掉一条环边,使其变成一颗 ...
- Codeforces 835 F Roads in the Kingdom(树形dp)
F. Roads in the Kingdom(树形dp) 题意: 给一张n个点n条边的无向带权图 定义不便利度为所有点对最短距离中的最大值 求出删一条边之后,保证图还连通时不便利度的最小值 $n & ...
- codeforces 427 div.2 F. Roads in the Kingdom
F. Roads in the Kingdom time limit per test 2 seconds memory limit per test 256 megabytes input stan ...
- Codeforces 835F Roads in the Kingdom (环套树 + DP)
题目链接 Roads in the Kingdom 题意 给出一个环套树的结构,现在要删去这个结构中的一条边,满足所有点依然连通. 删边之后的这个结构是一棵树,求所有删边情况中树的直径的最小值. 显 ...
- Codeforces 835F Roads in the Kingdom - 动态规划
题目传送门 传送点I 传送点II 传送点III 题目大意 给定一颗基环树,要求删去其中一条边,使得剩下的图形是一棵树,并且最长路的长度最短,求最长路的最短长度. 路径可以分为两部分:跨过环 和 在树内 ...
- Roads in the Kingdom CodeForces - 835F (直径)
大意: 给定一个基环树, 求删除一条环上的边使得直径最小. 直径分两种情况 环上点延伸的树内的直径 两个环上点的树内深度最大的点匹配 第一种情况直接树形dp求一下, 第二种情况枚举删除的环边, 线段树 ...
- 【CodeForces】835F Roads in the Kingdom
一.题目 题目描述 王国有\(n\)座城市与\(n\)条有长度的街道,保证所有城市直接或间接联通,我们定义王国的直径为所有点对最短距离中的最大值,现因财政危机需拆除一条道路并同时要求所有城市仍然联通, ...
- 【Codeforces 1110D】Jongmah FST分析
Codeforces 1110 D FST分析 dotorya.FizzyDavid.MofK.gamegame.matthew99.chokudai.eddy1021.DBradac.Happy_N ...
- CodeForces Roads not only in Berland(并查集)
H - Roads not only in Berland Time Limit:2000MS Memory Limit:262144KB 64bit IO Format:%I64d ...
随机推荐
- Linux使用sshfs通过ssh挂载远端机器
Linux使用sshfs通过ssh挂载远端机器 今天自己的开发机器突然磁盘剩余空间报警,再弄一块硬盘不太现实,于是想到了公司有一台机器上挂了一个大的磁盘,于是,我把目标偷偷锁定到了那个机器上. 折腾了 ...
- 防止前端脚本JavaScript注入
在使用ajax进行留言的时候,出现了一个问题.因为留言内容写完之后,通过ajax提交内容,同时使用js把留言的内容添加到页面上来.浏览留言的时候也是通过ajax请求,然后再显示的.这样,如果有人在留言 ...
- 神经网络中的Softmax激活函数
Softmax回归模型是logistic回归模型在多分类问题上的推广,适用于多分类问题中,且类别之间互斥的场合. Softmax将多个神经元的输出,映射到(0,1)区间内,可以看成是当前输出是属于各个 ...
- HttpWebRequest传值
From:发送方 class Program { static void Main(string[] args) { string strId = "zhangsan"; &quo ...
- xcomponent web 跨域组件介绍
1. 项目地址: https://github.com/krakenjs/xcomponent 2. 支持的特性 a. Render an iframe or popup on a different ...
- IDEA生成serialVersionUID的警告
默认情况下Intellij IDEA是关闭了继承了java.io.Serializable的类生成serialVersionUID的警告.如果需要ide提示生成serialVersionUID,那么需 ...
- delphi 2010安装unidac
UniDAC是一个功能强大的非可视化跨数据库的数据访问组件,可用于Delphi,Delphi for .NET,C++Builder,and Lazarus (Free Pascal).它提供了对流行 ...
- substr的学习(PHP学习)
substr的用法: 首先看PHP手册 ,手册上是这样说的 string substr ( string $string , int $start [, int $length ] ) 执行subst ...
- gen_server的一些心得
gen_server并不是我原来概念中的tcp_server或者udp_server的概念,只是一个纯粹的消息服务器,另外,附上它的一些回调函数的简单说明参考地址 http://hi.baidu.co ...
- 【转】jmeter压力测试
jmeter压力测试 Apache JMeter是Apache组织开发的基于Java的压力测试工具.用于对软件做压力测试,它最初被设计用于Web应用测试但后来扩展到其他测试领域, 是压力测试的首选软件 ...