Monkeys
Time Limit: 2000/1000 MS (Java/Others) Memory Limit:153428/153428 K (Java/Others)
Total Submission(s): 772 Accepted Submission(s): 252
Problem Description
There is a tree having N vertices. In the tree there are K monkeys (K <= N). Avertex can be occupied by at most one monkey. They want to remove some edges and leave minimum edges, but each monkey must be connected to at least
oneother monkey through the remaining edges.
Print the minimum possible number of remaining edges.
Input
The first line contains an integer T (1 <= T <= 100), the number of testcases.
Each test case begins with a line containing two integers N and K (2 <= K<= N <= 100000). The second line contains N-1 space-separated integersa1,a2,…,aN−1,
it means that there is an edge between vertex ai and vertex i+1 (1 <=ai <= i).
Output
For each test case, print the minimum possible number of remaining edges.
Sample Input
2
4 4
1 2 3
4 3
1 1 1
Sample Output
2
2
【题意】
给出一棵有n个节点的树,现在需要你把k只猴子放在节点上,每个节点最多放一只猴子,且要求每只猴子必有一只另外的猴子通过一条边与它相连,问最少用多少条边能达到这个要求。
【思路】
利用贪心的思维,显然我们应该先选择性价比最高的,即一条边连接两个点的情况。计算出这样的边的条数ans,如果ans*2>=k,结果就是(k+1)/2,否则剩下来没有安排的猴子每一只需要多一条边,即结果为ans+k-2 * ans.
【PS】这题卡读入时间,故必须用fread读入挂优化。
方法一:用类拓扑排序的方法,找连接两个度数为1且没用被用过的点的边数,每次找到一条边后注意更新度数。
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scan_d(T);while(T--)
typedef long long ll;
const int maxn = 100005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
int vis[maxn];
vector<int>vec[maxn];
using namespace std;
namespace IO
{
const int U=40*1024*1024;
char buf[U];
int ptr,sz;
void begin()
{
ptr=0;
sz=fread(buf,1,U,stdin);
}
template<typename T>
inline bool scan_d (T&t)
{
while (ptr<sz&&buf[ptr]!='-'&&(buf[ptr]<'0'||buf[ptr]>'9'))ptr++;
if (ptr>=sz)return false;
bool sgn = false;
if (buf[ptr]=='-')sgn = true,ptr++;
for (t = 0; ptr<sz&&'0'<= buf[ptr] &&buf[ptr] <= '9'; ptr++)
t=t*10+buf[ptr] - '0';
if (sgn)t=-t;
return true;
}
}
using namespace IO;
int main()
{
int n,k;
IO::begin();
rush()
{
mst(vis,0);
scan_d(n);
scan_d(k);
for(int i=1; i<=n; i++)
{
vec[i].clear();
}
for(int i=2; i<=n; i++)
{
int x;
scan_d(x);
vec[x].push_back(i);
vec[i].push_back(x);
}
queue<int>q;
for(int i=1; i<=n; i++)
{
if(vec[i].size()==1) //将度数为1的点入队列
{
q.push(i);
}
}
int ans=0;
while(q.size())
{
int u=q.front();
q.pop();
if(vec[u].size()!=0)
{
int v=vec[u][0];
if(vis[u]==0&&vis[v]==0)
{
ans++;
vis[u]=1;
vis[v]=1;
}
vec[v].erase(remove(vec[v].begin(),vec[v].end(),u),vec[v].end()); //更新度数
if(vec[v].size()==1) //将度数为1的点入队列
q.push(v);
}
}
//printf("**%d\n",ans);
if(ans*2>=k)
{
printf("%d\n",(k+1)/2);
}
else
{
ans=ans+k-ans*2;
printf("%d\n",ans);
}
}
return 0;
}
方法二:树形DP
上面说到,我们尽量要使一个点只匹配一个点,很容易想到二分匹配中的最大匹配数,而最大匹配数即为最小点覆盖数,可用树形DP解决。
我们用dp[i][1]表示选择i这个顶点(初始化dp[root][1]为1)的最小点覆盖数,那么它的儿子节点son[i]可以被选择也可以不被选择。
状态转移方程为
dp[i][1]+=min(dp[son[i]]][0],dp[son[i]]][1]).
用dp[i][0]表示不选择i这个顶点(初始化dp[root][0]为0)的最小点覆盖数,显然它的儿子节点son[i]就必须被选择了。
状态转移方程为
dp[i][0]+=dp[son[i]]][1].
最终求出最小点覆盖数(最大匹配数)即为min(dp[1][0],dp[1][1]),也就求出了ans,然后跟上面的步骤相同。
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scan_d(T);while(T--)
typedef long long ll;
const int maxn = 100005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
int n,k;
int dp[maxn][2];
vector<int>vec[maxn];
using namespace std;
namespace IO
{
const int U=40*1024*1024;
char buf[U];
int ptr,sz;
void begin()
{
ptr=0;
sz=fread(buf,1,U,stdin);
}
template<typename T>
inline bool scan_d (T&t)
{
while (ptr<sz&&buf[ptr]!='-'&&(buf[ptr]<'0'||buf[ptr]>'9'))ptr++;
if (ptr>=sz)return false;
bool sgn = false;
if (buf[ptr]=='-')sgn = true,ptr++;
for (t = 0; ptr<sz&&'0'<= buf[ptr] &&buf[ptr] <= '9'; ptr++)
t=t*10+buf[ptr] - '0';
if (sgn)t=-t;
return true;
}
}
using namespace IO;
void dfs(int u,int pre)
{
dp[u][0]=0; //不选择这个点的最小点覆盖数
dp[u][1]=1; //选择这个点的最小点覆盖数
for(int i=0;i<vec[u].size();i++)
{
int v=vec[u][i];
if(v==pre) continue;
dfs(v,u);
dp[u][1]+=min(dp[v][0],dp[v][1]);
dp[u][0]+=dp[v][1];
}
}
int main()
{
IO::begin();
rush()
{
scan_d(n);
scan_d(k);
mst(dp,0);
for(int i=1; i<=n; i++)
{
vec[i].clear();
}
for(int i=2; i<=n; i++)
{
int x;
scan_d(x);
vec[x].push_back(i);
vec[i].push_back(x);
}
dfs(1,-1);
int ans=min(dp[1][0],dp[1][1]);
if(ans*2>=k)
{
printf("%d\n",(k+1)/2);
}
else
{
ans=ans+k-ans*2;
printf("%d\n",ans);
}
}
return 0;
}