您的位置:首页 > 其它

树形DP+并查集+左偏树, HDU-5575,Discover Water Tank,2015上海现场赛D题

2017-01-03 17:03 435 查看
只是ACM/IICPC 2015 上海区域赛的一道题。原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=5575

题目描述

N-1个木板把一个水箱划分成了N部分(从左到右形成了编号为[1,N]的N个小水箱)。这些木板高度不尽相同。因为水往低处流和木板相隔,所以整个大水箱中有若干个高低不同的水平面。

接下来进行M次探测,探测指定地方指定高度有没有水。这些探测中包含错误结果,求可能的正确探测数目的最大值。

输入

T组数据。

每组数据以N和M开始。N-1为木板个数,M代表探测次数。

接下来一行是N-1个整数,表示每块木板的高度。

然后是M行,(x,y,z)三个数表示在编号为x的水箱中测试y+0.5米高的地方是否有水,z=0表示没有水, z=1表示有水。

输出

可能的正确探测数目的最大值。

Sample Input

2

3 4

3 4

1 3 1

2 1 0

2 2 0

3 3 1

2 2

2

1 2 0

1 2 1

Sample Output

Case #1: 3

Case #2: 1

提示

对于sample input中的第一组数据,认为第一次探测错误,那么可能的正确探测数目的最大值就就是3。

ac代码

//HDOJ_5575_AC
#include
#include
#include
#include

using namespace std;

//多组测试case时,可以复用数组,不需动态申请用完再回收。
const int MAX_N = 1e5 + 1;
const int MAX_M = MAX_N * 2;
const int INF = 1e10 + 1;
typedef pair HeightAndTankNo;
struct LeftistTreeNode;

int caseNum = 0, caseNo = 0, answer = 0;
int n, detectNum;
int leftHeightOfTank[MAX_N], rightHeightOfTank[MAX_N];
//第i个水箱的相邻水箱编号, 水箱合并时会用
int LeftTank[MAX_N],RightTank[MAX_N];
//有水探测,无水探测
int hasWaterDetect[MAX_N], noWaterDetect[MAX_N];
//有水探测对应的数组
vector hasWaterDetectVector;

//leftistTree[i] 表示 第i个水箱对应的左偏树节点编号
int leftistTree[MAX_N];

int unionFindSet[MAX_N];
//左偏树节点编号,通过自增来用
int lefttistNodeNo;

//node[i] means i-th node
LeftistTreeNode *nodes;
struct LeftistTreeNode {
int value;
int dist;
int lchild, rchild;

LeftistTreeNode() {
value = lchild = rchild = 0;
dist = 1;
}
static void init() {
if (nodes == NULL)
nodes = new LeftistTreeNode[MAX_M];
for (int i = 0; i < MAX_M; i++) {
nodes[i].value = nodes[i].lchild = nodes[i].rchild = 0;
nodes[i].dist = 1;
}
}
/**
*
* @param a tree
* @param b tree
* @return new root
*/
static int merge(int a, int b) {
if (a == 0)
return b;
if (b == 0)
return a;
//小顶堆
if (nodes[a].value > nodes[b].value)
swap(a, b);
nodes[a].rchild = merge(nodes[a].rchild, b);
if (nodes[nodes[a].lchild].dist < nodes[nodes[a].rchild].dist)
swap(nodes[a].lchild, nodes[a].rchild);
nodes[a].dist = nodes[nodes[a].rchild].dist + 1;
return a;
}
/**
* @param a tree
* @param b new node
* @return new root
*/
static int insert(int a, int b) {
return merge(a, b);
}
/**
* delete the top element and return new root
*/
static int pop(int a) {
return merge(nodes[a].lchild, nodes[a].rchild);
}
};

//并查集
struct UnionFindSet {
static void init() {
for (int i = 0; i < MAX_N; i++)
unionFindSet[i] = i;
}
static int findParent(int x) {
return unionFindSet[x] == x ?
x : unionFindSet[x] = findParent(unionFindSet[x]);
}
static void merge(int x, int y) {
unionFindSet[y] = unionFindSet[x];
}
};

struct Main {

void read() {
cin >> n >> detectNum;
//第i个木板的高度, 也就是第i个水箱的右挡板的高度
for (int i = 1; i < n; i++)
cin >> rightHeightOfTank[i];
}
void init() {
answer = 0;
UnionFindSet::init();
LeftistTreeNode::init();
hasWaterDetectVector.clear();

leftHeightOfTank[1] = rightHeightOfTank
= INF;
LeftTank
= n - 1;
lefttistNodeNo = 0;

for (int i = 1; i < n; i++) {
leftHeightOfTank[i + 1] = rightHeightOfTank[i];
LeftTank[i] = i - 1;
RightTank[i] = i + 1;
}
for (int i = 1; i <= n; i++) {
hasWaterDetect[i] = noWaterDetect[i] = 0;
leftistTree[i] = 0;
}
}
void calc() {
while (detectNum--) {
int x, y, z;
//(x,y,z)表示	在编号为x的水箱中测试y+0.5 米高的地方是否有水,z=0表示没有水, z=1表示有水。
cin >> x >> y >> z;
//有水,放数组
if (z)
hasWaterDetectVector.push_back(HeightAndTankNo(y + 0.5, x));
//没水
else {
//每次无水探测, 答案+1
answer++;

//每次无水探测对应一棵 左偏树
nodes[++lefttistNodeNo].value = y;

//x号水箱无水探测的最低位置
leftistTree[x] =
leftistTree[x] != 0 ?
LeftistTreeNode::merge(leftistTree[x],
lefttistNodeNo) :
lefttistNodeNo;
}
} //while (detectNum--)
sort(hasWaterDetectVector.begin(), hasWaterDetectVector.end());
for (unsigned int i = 0; i < hasWaterDetectVector.size(); i++) {
int tankNo = UnionFindSet::findParent(
hasWaterDetectVector[i].second);
//探测到有水的高度
float height = hasWaterDetectVector[i].first;
//水位向左溢出
while (height > leftHeightOfTank[tankNo]) {
mergeTank(tankNo, LeftTank[tankNo]);
tankNo = UnionFindSet::findParent(tankNo);
}
//水位向右溢出
while (height > rightHeightOfTank[tankNo]) {
mergeTank(tankNo, RightTank[tankNo]);
tankNo = UnionFindSet::findParent(tankNo);
}
while (leftistTree[tankNo] != 0
&& nodes[leftistTree[tankNo]].value < height) {
leftistTree[tankNo] = LeftistTreeNode::pop(leftistTree[tankNo]);
noWaterDetect[tankNo]++;
}

if (++hasWaterDetect[tankNo] >= noWaterDetect[tankNo]) {
answer += (hasWaterDetect[tankNo] - noWaterDetect[tankNo]);
hasWaterDetect[tankNo] = noWaterDetect[tankNo] = 0;
}
}
}
void output() {
cout << "Case #" << ++caseNo << ": " << answer << endl;
}
void mergeTank(int x, int y) {
x = UnionFindSet::findParent(x);
y = UnionFindSet::findParent(y);
if (x == y)
return;
UnionFindSet::merge(x, y);
//x在左,y在右
if (x < y) {
rightHeightOfTank[x] = rightHeightOfTank[y];
LeftTank[RightTank[x]] = x;
RightTank[x] = RightTank[y];
} else {
leftHeightOfTank[x] = leftHeightOfTank[y];
RightTank[LeftTank[x]] = x;
LeftTank[x] = LeftTank[y];
}
leftistTree[x] = LeftistTreeNode::merge(leftistTree[x], leftistTree[y]);
noWaterDetect[x] += noWaterDetect[y];
hasWaterDetect[x] += hasWaterDetect[y];
}

void entry() {
cin >> caseNum;
while (caseNum--) {
read();
init();
calc();
output();
}
}

};

int main() {
std::ios::sync_with_stdio(false);
//HDOJ系统在运行时会注入ONLINE_JUDGE这个宏定义
#ifndef ONLINE_JUDGE
freopen("d:\\code-practice\\in.txt", "r", stdin);
#endif
Main().entry();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: