Contestants Division(poj 3140)
题目类型:深度优先搜索
原题链接
题目大意:给出一棵树的结构,每个节点都对应着一个值。选择一条边,将该树分为两个部分,使得两个部分的节点和之差最小。并输出差的最小值。
虽然M的范围比N大,但是根据题意,形成的是一棵树,因此M=N-1。
解题思路:
使用类似于线段树的思想,每一个树节点维护一个变量,为以该节点为根节点的子树的节点数x。那么断开其和父亲的连接,就会形成两棵子树,分别为x和S-x。
那么最终是一个深度优先遍历的过程,实践复杂度为0(N)
#include<iostream>
#include<algorithm>
using namespace std;
#define NMAX 100005
int N, M;
long long num[NMAX];
int is[NMAX];
long long sum[NMAX];
long long res = 0x3f3f3f3f3f3f3f3f;
struct Edge {
int from, to, nxt;
}E[2*NMAX];
int head[NMAX], cnt = 1;
long long S = 0; //注意这个也应该是long long类型
void add(int s, int t) {
E[cnt].from = s, E[cnt].to = t, E[cnt].nxt = head[s];
head[s] = cnt++;
E[cnt].from = t, E[cnt].to = s, E[cnt].nxt = head[t];
head[t] = cnt++;
}
void dfs(int u) {
is[u] = 1;
sum[u] = num[u];
for (int i = head[u]; i; i = E[i].nxt) {
int v = E[i].to;
if (!is[v]) {
dfs(v);
sum[u] += sum[v];
}
}
if (u != 1) {
long long sub = sum[u] - (S - sum[u]);
if (sub < 0) sub = -sub;
if (sub < res) res = sub;
}
}
int main() {
int c = 1;
while (1) {
scanf_s("%d%d", &N, &M);
if (!N && !M) break;
memset(head, 0, sizeof(head));
memset(is, 0, sizeof(is));
memset(sum, 0, sizeof(sum));
cnt = 1;
S = 0;
for (int i = 1; i <= N; i++) {
scanf_s("%lld", &num[i]);
S += num[i];
}
for (int i = 0; i < M; i++) {
int s, t;
scanf_s("%d%d", &s, &t);
add(s, t);
}
res = S;
dfs(1);
printf("Case %d: %lld\n",c++, res);
}
}
tip:
1.注意数据类型的表示,不要弄错了
The Lost House(poj 2057)
原题链接
题目大意:一棵具有N(1000)个节点的树,节点上可能会有一个虫子。对于一种遍历(指的是遍历所有的叶子节点),求其路径长度的最小值。由于虫子的存在,会使得总的路径长度发生变化。同时,需要注意到,各个节点的遍历顺序会直接影响到总的路径长度。
限定在一个所谓的爬行顺序上可能会有局限
//初步实现->WA->没有认识到本质
#include<iostream>
using namespace std;
#define NMAX 1003
int N;
struct Edge {
int from, to, nxt;
}E[NMAX];
int head[NMAX], cnt = 1, worm[NMAX];
int dp[NMAX][3];
void add(int u, int v) {
E[cnt].from = u, E[cnt].to = v, E[cnt].nxt = head[u];
head[u] = cnt++;
}
void dfs(int u) {
int j = 1;
int k = 0;
for (int i = head[u]; i; i = E[i].nxt) {
int v = E[i].to;
dfs(v);
dp[u][2] += dp[v][2];
if (!worm[v]) dp[u][0] += (2 + dp[v][0]);
else dp[u][0] += 2;
if (!worm[v]) dp[u][1] += (j*dp[v][2] + dp[v][0] + dp[v][1]);
else dp[u][1] += (j*dp[v][2] + dp[v][1]); //需要对dp[v][2]进行排序,且有虫的必须位于前面
j += 2;
if (dp[v][0] > k) k = dp[v][0];
}
if (dp[u][2]) dp[u][1] -= k;
else dp[u][2] = 1;//代表的是叶子节点
}
int main() {
while (1) {
scanf_s("%d", &N);
memset(head, 0, sizeof(head));
memset(worm, 0, sizeof(worm));
memset(dp, 0, sizeof(dp));
cnt = 1;
for (int i = 1; i <= N; i++) {
int fa;
char c;
scanf_s("%d %c", &fa, &c);
if (fa != -1) add(fa, i);
if (c == 'Y') worm[i] = 1;
else if (c == 'N') worm[i] = 0;
else cout << "WRONG" << endl;
}
dfs(1);
printf("%.4f\n", (double)dp[1][1] / dp[1][2]);
}
}
关于全排序中的贪心问题
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define NMAX 1003
int N;
struct Edge {
int from, to, nxt;
}E[NMAX];
int head[NMAX], cnt = 1, worm[NMAX];
int dp[NMAX][3];
void add(int u, int v) {
E[cnt].from = u, E[cnt].to = v, E[cnt].nxt = head[u];
head[u] = cnt++;
}
void dfs(int u) {
vector<pair<double, int>> xu;
int j = 1;
int k = 0;
for (int i = head[u]; i; i = E[i].nxt) {
int v = E[i].to;
dfs(v);
dp[u][2] += dp[v][2];
if (!worm[u]) dp[u][0] += (2 + dp[v][0]);
xu.push_back(make_pair((double)(dp[v][0]+2)/dp[v][2],v));
}
//若(u,v)则house在v上应为dp[v][2]*(k+1)+dp[v][1] k为不在前面的所有的路程 k=sum(dp[vi][0]+2)
//对于序列x1,x2,x3,...xi,xj...交换xi和xj,
//按照(dp[xi][0]+2)/dp[xi][2]从小到大的顺序
if (dp[u][2]) {
sort(xu.begin(), xu.end());
long long ssum = 0;
for (int i = 0; i < xu.size(); i++) {
int v = xu[i].second;
dp[u][1] += (dp[v][2] * (ssum+1) + dp[v][1] );
ssum += (dp[v][0] + 2);
}
}
else dp[u][2] = 1;//代表的是叶子节点
}
int main() {
while (1) {
scanf_s("%d", &N);
if (!N) break;
memset(head, 0, sizeof(head));
memset(worm, 0, sizeof(worm));
memset(dp, 0, sizeof(dp));
cnt = 1;
for (int i = 1; i <= N; i++) {
int fa;
char c;
scanf_s("%d %c", &fa, &c);
if (fa != -1) add(fa, i);
if (c == 'Y') worm[i] = 1;
else if (c == 'N') worm[i] = 0;
else cout << "WRONG" << endl;
}
dfs(1);
printf("%.4f\n", (double)dp[1][1] / dp[1][2]);
}
}