1题目描述很简单。。其实就是求最小点覆盖。。但是。。。他明确说明是树。。如果用矩阵存储。。然后用匈牙利,显然不仅浪费空间。还浪费时间。。TLE了。。。
然后用邻接表。。可以AC了。。。
但是效率还是不高啊。。
这是一道最典型节点覆盖问题,这类可以用树形DP解。
对于顶点i,有两种状态,有士兵,没士兵,将这种状态下以i为根的子树的总士兵数为dp[i][0],dp[i][1]。
显然,如果i点没有士兵,那么它的所有子节点一定要有士兵,否则中间的线不能被覆盖。
所以dp[i][0]=dp[j0][1]+dp[j1][1]+...+dp[jk][1],其中j1,j2,...,jk是i的所有子节点。
如果i点有士兵,那么它的任意子节点都可以有士兵或没有士兵。
所以dp[i][1]=min(dp[j0][0],dp[j0][1])+min(dp[j1][0],dp[j1][1])+...+min(dp[jk][0],dp[jk][1])+1.
对于叶子节点,dp[i][0]=0,dp[i][1]=1
以上就是动态规划的状态转移方程。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<limits.h>
#define MIN(a,b) ((a)<(b)?(a):(b))
#define N 1505
#define M 11
typedef struct
{
int sum0,sum1;
}Sum;
Sum save;
typedef struct node
{
int count;
struct node * next[M];
}tree;
Sum dp(tree * root)
{
Sum s;
int i;
s.sum0=0;
s.sum1=1;
for(i=0;i<root->count;i++)
{
save=dp(root->next[i]);
s.sum0+=save.sum1;
s.sum1+=MIN(save.sum1,save.sum0);
}
return s;
}
int main(void)
{
int n,a,m,i,j,v;
tree t[N],*root;
Sum ans;
while(scanf("%d",&n)!=EOF)
{
memset(t,0,sizeof(t));
root=NULL;
for(i=0;i<n;i++)
{
scanf("%d:(%d)",&a,&m);
t[a].count=m;
if(root==NULL)
{
root=&t[a];
}
for(j=0;j<m;j++)
{
scanf("%d",&v);
t[a].next[j]=&t[v];
}
}
ans=dp(root);
printf("%d\n",MIN(ans.sum0,ans.sum1));
}
return 0;
}