500:最多50个点的一棵树,每条边代表一盏灯,有两种状态,开或关,还有两种属性,重要或者不重要
定义一种操作是选择一条路径,将路径上的边的开关状态取反。问最少需要多少次操作才能使得每条重要的边都处于开的状态。
显然,所有的不重要的边都可以合并起来,然后搞成一棵新的树,每条边都是重要的,然后再YY一下,每条已经开的边不需要被覆盖到了,因为取反后还是要取反回来的,这样跟不去取反是一样的,所以现在这棵树被拆分成了若干棵树,每棵树全是需要取反的边,根据奇偶性贪心取即可。
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <ctime>
using namespace std;
class TurnOnLamps
{
public:
int minimize(vector <int> roads, string initState, string isImportant);
};
int fa[55];
int g[55][55];
int find(int x) {
return fa[x] == x ? x : find(fa[x]);
}
void Union(int x,int y) {
int fx = find(x);
int fy = find(y);
if(fx != fy) {
fa[fx] = fy;
}
}
bool vis[55];
int n;
int owe[55];
int ans;
void dfs(int u,int f,int state) {
vis[u] = true;
int cnt = 0, sum = 0;
for(int i = 1; i < n; i++) {
if(g[u][i] != -1 && !vis[i]) {
dfs(i, u, g[u][i]);
cnt ^= g[u][i];
sum += g[u][i];
if(g[u][i] == 0) {
ans += owe[i];
}
}
}
owe[u] = cnt;
ans += sum / 2;
}
int TurnOnLamps::minimize(vector <int> roads, string initState, string isImportant){
ans = 0;
memset(owe, 0, sizeof(owe));
for(int i = 0; i < 50; i++) fa[i] = i;
memset(g, -1, sizeof(g));
n = roads.size() + 1;
for(int i = 0; i < roads.size(); i++) {
int a = roads[i];
int b = i + 1;
if(isImportant[i] == '0') {
Union(a, b);
}
}
for(int i = 0; i < roads.size(); i++) {
int a = roads[i];
int b = i + 1;
int x = find(a);
int y = find(b);
if(x != y) g[x][y] = g[y][x] = (initState[i] == '0' );
}
int root = find(0);
memset(vis, false, sizeof(vis));
dfs(root, -1, -1);
ans += owe[root];
return ans;
}
// Powered by FileEdit
// Powered by TZTester 1.01 [25-Feb-2003]
// Powered by CodeProcessor