前面也做了一道很像的题,那道题只要求放置的数目最少,要求覆盖的是点。在这题中,要求覆盖的是边,不但要求放着的数目最少,更要求覆盖两次的边最多。因此贪心法不再适用,最少要多少个点可以贪心求出,但是同时要求覆盖两次的边要最多却不是贪心法能够解决的,无论如何都是逃不过动态规划的。所以一开始用贪心做,做到最后发现方法是错的。看了大白书才开始用动态规划。
大白书上的方法好奇怪,dp[i][j]代表i节点的父亲节点是否放灯。好难理解。我用dp[i][j]代表i节点是否放灯思路十分清晰,很快就AC了。看了好久书才理解大白书上的做法,感觉不是很好。
大白书上最值得学习的地方就是如何定义动态规划的值。
本题的优化目标有两个,一个是要求灯数a应尽量少,另一个是要求被两盏灯同时照亮的边数b应尽量大。如果灯数尽量少,那么就不会出现一条边被照亮3次的低效情况,因此总边数=被照亮1次的边数+被照亮2次的边数。即M=b+c。我们要求b尽量大,那么要求c尽量小即可。这样就把两个优化目标都转化成了尽量小。题目要求在a尽量小的前提下c尽量小,有个技巧是将二者组合成一个量aM+c,M是一个比(c的最大理论值-c的最小理论值)还要大的数,这样当aM+c最小时,一定是先保证了a最小,再保证c最小。在本题中M取2000。
我的方法:
若u不放灯,那么值就是所有(子节点v放灯的值+1)的和。+1是因为uv之间只被照亮1次。
若u放灯,那么所以子节点爱放不放,如果不放,就要+1,然后取最小值求和就好了。
设根为root,答案就是min(dp[root][0],dp[root][1])。
思路又清晰,编码又简单,自己觉得比书上好多了。
我的方法
#include<bits/stdc++.h>
#define maxn 1010
#define MAX 2000
using namespace std;
int N,M;
vector<int>MAP[maxn];
int dp[maxn][2];
bool vis[maxn];
void dfs(int u,int f)
{
vis[u]=true;
dp[u][0]=0;
dp[u][1]=MAX;
for(unsigned int i=0;i<MAP[u].size();i++)
{
int v=MAP[u][i];
if(v==f) continue;
dfs(v,u);
dp[u][0]+=dp[v][1]+1;
dp[u][1]+=min(dp[v][0]+1,dp[v][1]);
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&N,&M);
for(int i=0;i<N;i++)
{
MAP[i].clear();
vis[i]=0;
}
int u,v;
for(int i=0;i<M;i++)
{
scanf("%d %d",&u,&v);
MAP[u].push_back(v);
MAP[v].push_back(u);
}
int a,b,c;
a=b=c=0;
for(int i=0;i<N;i++)
{
if(!vis[i])
{
dfs(i,-1);
int ans=min(dp[i][0],dp[i][1]);
a+=ans/MAX;
c+=ans%MAX;
}
}
b=M-c;
printf("%d %d %d\n",a,b,c);
}
return 0;
}
书上的方法
#include<bits/stdc++.h>
#define maxn 1010
#define MAX 2000
using namespace std;
int N,M;
vector<int>MAP[maxn];
bool vis[maxn];
int dp[maxn][2];
void dfs(int u,int f)
{
vis[u]=true;
int jc1,jc2;
jc1=jc2=0;
for(unsigned int i=0;i<MAP[u].size();i++)
{
int v=MAP[u][i];
if(v==f) continue;
dfs(v,u);
jc1+=dp[v][0];
jc2+=dp[v][1];
}
jc2+=MAX;
dp[u][0]=dp[u][1]=jc2;
if(f!=-1) dp[u][0]++;
if(f==-1)
{
dp[u][0]=min(dp[u][0],jc1);
dp[u][1]=min(dp[u][1],jc1);
}
else dp[u][1]=min(dp[u][1],jc1+1);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&N,&M);
for(int i=0;i<N;i++)
{
MAP[i].clear();
vis[i]=0;
}
int u,v;
for(int i=0;i<M;i++)
{
scanf("%d %d",&u,&v);
MAP[u].push_back(v);
MAP[v].push_back(u);
}
int a,b,c;
a=b=c=0;
for(int i=0;i<N;i++)
{
if(!vis[i])
{
dfs(i,-1);
int ans=dp[i][0];
a+=ans/MAX;
c+=ans%MAX;
}
}
b=M-c;
printf("%d %d %d\n",a,b,c);
}
return 0;
}