Strategic Game
Time limit:20000/10000 MS (java/others) Memory limit:65536/32768 K (java/others)
Total submission (s): 5313 Accepted Submission (s): 2452
Problem Description Bob enjoys playing computer games, especially strategic games, but sometimes he cannot find the Soluti On fast enough and then he's very sad. Now he has the following problem. He must defend a medieval city, the roads of which form a tree. He has to put the minimum number of soldiers in the nodes so, they can observe all the edges. Can you help him?
Your program should find the minimum number of soldiers that Bob have to put for a given tree.
The input file contains several data sets in text format. Each data set represents a tree with the following description:
The number of nodes
The description of each node in the following format
Node_identifier: (number_of_roads) node_identifier1 node_identifier2 ... node_identifier
Or
Node_identifier: (0)
The node identifiers is integer numbers between 0 and n-1, for n nodes (0 < n <= 1500). Every edge appears only once in the input data.
For example for the tree:
The solution is a soldier (at the Node 1).
The output should is printed on the standard output. For each given input data set, print one, integer number in a, gives the result (the minimum number of sold Iers). An example are given in the following table:
Sample Input
4 0: (1) 1 1: (2) 2 3 2: (0) 3: (0) 5 3: (3) 1 4 2 1: (1) 0 2: (0) 0: (0) 4: (0)
Sample Output
1 2
Source Southeastern Europe 2000
This problem is very classical, and the solution is more.
First of all, the first solution, that is, the minimum vertex coverage problem. The König theorem theorem shows that the minimum vertex covering number of a binary graph is equal to the maximum matching number of two.
The proof of König theorem is much more on the net. We can find a search for Baidu. The tree in question can be used as a bipartite graph because if you start from a point, you can divide the whole tree into odd and even points layers. Because the tree is a special kind of diagram. N points are connected by a (n-1) bar edge. This assumes that a point is the root of the tree, assuming that the Benquan value between the points is 1. Then the tree is traversed from the root, and all points are divided into two sets according to the parity of the paths of each point to the root. Odd points alternately appear with even points. Assuming that the odd points are connected with the even points, the even points continue and the odd points of the next layer are connected. This is the same as the two points in the same set of points between the infinite, not the same set of points between the adjacent to coincide. Therefore satisfies the nature of the binary graph. can also be solved by using the maximum matching of the binary graph. This method of dividing points into odd points is much like the odd-even pruning in search pruning. The classification of points in parity pruning is the same as this method.
About the parity pruning, see http://blog.csdn.net/eclipse88/article/details/6475127
Classic examples of parity pruning: HDU1010
The following continues to say the maximum match of the binary graph. Due to the completion of the two-dimensional graph (no direction graph), the edge is increased to twice times the original edge. So the final result will be divided by 2.
Dichotomy minimum Vertex overlay = bidirectional binary graph max match/2
binary Graph maximum matching code:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <
Vector> #define CLR (x) memset (x,0,sizeof (x)) #define __CLR (x) memset (x,-1,sizeof (x)) using namespace Std;
vector<int>g[1510];
BOOL vis[1510];
int match[1510];
bool Dfs (int u) {for (int i=0;i<g[u].size (); i++) {int t=g[u][i];
if (!vis[t]) {vis[t]=1; if (match[t]==-1| |
DFS (Match[t])) {match[t]=u;
return true;
}}} return false;
} int main () {//ios_base::sync_with_stdio (0);
int n;
while (~SCANF ("%d", &n)) {for (int i=0; i<n; i++) {int m,k;
scanf ("%d: (%d)", &m,&k);
for (int j=0; j<k; J + +) {int A;
scanf ("%d", &a);
G[m].push_back (a);
G[a].push_back (m);
}
} int res=0;
__CLR (match);
for (int i=0; i<n; i++) {CLR (VIS);
if (Dfs (i)) res++;
} printf ("%d\n", RES/2);
for (int i=0;i<1510;i++) g[i].clear ();
}
}
Method Two is a tree-shaped DP. The subject is also one of the classic examples of tree-shaped DP.
In the same way, we can also layer the entire tree and then iterate through it. Of course, before that, you have to turn a tree without a root into a tree. Since the tree is inherently connected, all nodes in the entire tree can be accessed from any point of view. First we identify a point as the root and then perform a DFS traversal of the entire tree. For DP, there are two decisions for a node, choose this node or uncheck it.
If you do not select the node as the overlay, you must select all its child nodes as the overlay to overwrite the edges that are attached to it.
If you select this node as the overlay, consider whether to select its child nodes, we take advantage, so we select the minimum value of the child nodes under both decisions.
Also have to explain is because of the natural fine nature of the tree, side for (n-1) bar, very few. So it's wise to store the edges directly. Store edges with or adjacency table storage. Select the head interpolation method when inserting edges.
Tree-shaped DP code:
#include <cstdio> #include <cstring> #include <iostream> #include <vector> #include <
Algorithm> #define CLR (x) memset (x,0,sizeof (x)) #define __CLR (x) memset (x,-1,sizeof (x)) using namespace Std; struct Edge {int to,next;}
E[1505];
int h[1505],dp[1505][2],num=1;
void Addedge (int u,int v) {e[num].to=v;
E[num].next=h[u];
h[u]=num++;
} void Dfs (int u) {int v,i;
dp[u][0]=0;
Dp[u][1]=1;
for (I=h[u];i!=-1;i=e[i].next) {v=e[i].to;
DFS (v);
DP[U][0]+=DP[V][1];
Dp[u][1]+=min (dp[v][0],dp[v][1]);
}} int main () {int n;
while (~SCANF ("%d", &n)) {__clr (h);
int rt=-1;
Num=1;
for (int i=1; i<=n; i++) {int u,k;
scanf ("%d: (%d)", &u,&k);
for (int j=1; j<=k; J + +) {int V;
scanf ("%d", &v);
Addedge (U,V); } if (rt==-1) rt=u;
} DFS (RT);
int res=min (dp[rt][0],dp[rt][1]);
printf ("%d\n", res);
}
}
Method three is greed. The greedy strategy is that the more points you choose, the higher the point value. The value of the point with a degree of 1 is minimal. Of course we can find the points that are connected to the points with a degree of 1 as the coverage point. and remove the edges that are attached to these points. This approach is very similar to the idea of topological sequencing. Consider the degree of the node, and record the degree of each node, after the decision is made to delete the edge operation, until the final point is not the degree of 1 so far. The queue can be implemented with the operation. The sense and topology sort really seems. Although the purpose is different, but the thought is interlinked.
The visit array is used to record whether the node has been accessed, ensuring that each node is accessed once. (Side is no edge)
Greedy Code:
#include <cstdio> #include <cstring> #include <iostream> #include <vector> #include <queue > #include <algorithm> #define CLR (x) memset (x,0,sizeof (x)) #define __CLR (x) memset (x,-1,sizeof (x)) #define PB
Push_back using namespace std;
vector<int>g[1505];
int indx[1505],vis[1505],n;
void Solve () {CLR (VIS);
Queue<int> Q;
for (int i=0; i<n; i++) if (indx[i]==1) Q.push (i);
int num=0;
while (!q.empty ())//Because it is a tree, has connectivity and does not cause a dead loop {int S=q.front ();
Q.pop ();
if (!vis[s]) {vis[s]=1;
for (int i=0; i<g[s].size (); i++) {int t=g[s][i];
if (!vis[t]) {num++; Vis[t]=1; Why is it not considered that the degree of the T point is equal to 1 or greater than 1? Imagine if it is equal to 1, then select the S point and the T point is OK,//But if it is greater than 1, then definitely choose t Point.
This is similar to the idea of König theorem theorem when choosing that match point more like for (int j=0; j<g[t].size (); j + +) {int tt=g[t][j];
indx[tt]--;
if (Indx[tt]==1&&!vis[tt]) Q.push (TT);
}}}}} printf ("%d\n", num);
} int main () {while (~SCANF ("%d", &n)) {CLR (indx);
for (int i=0; i<n; i++) {int u,k;
scanf ("%d: (%d)", &u,&k);
for (int j=1; j<=k; J + +) {int V;
scanf ("%d", &v);
G[U].PB (v);
G[V].PB (U);
indx[u]++;
indx[v]++;
}} if (N==1) {printf ("1\n");
Continue
} solve ();
for (int i=0;i<1505;i++) g[i].clear ();
}
}
The problem of a multi-solution, involving a wide range of knowledge, have to delve into it.