UVA - 10859 Placing Lampposts 放置街燈


Placing Lampposts

傳送門:https://vjudge.net/problem/UVA-10859

題目大意:給你一片森林,要求你在一些節點上放上燈,一個點放燈能照亮與之相連的所有的邊。問你最小化防止的燈數,在燈數相同的條件下,最大化兩個點都有燈的邊數。
題解:

  首先有一個套路,也是做了此題才知道的,很神奇啊。最小化燈的數量,我們設燈數為V1,把“最大化兩個點都有燈的邊數”轉化為“最下化只有一個點有燈的邊數”,設為V2,那么我們設Val=Eps*V1+V2。這樣只要DP一個值就可以了。Eps設成一個足夠大的值,保證Eps>sum{V2}。此題姑且設為2000。
  然后我們就可以DP了。樹上求解最優解,此題為森林,轉化為每棵樹的答案相加就可以了。那么怎么DP呢?
  設狀態DP[i]代表i節點與它的子樹以及連向父親的那一條邊的最小的Val。每一個節點有放燈與不放燈兩種狀態,但是我們發現,父親放不放燈會影響兒子放不放燈,那么我們再加上一維的狀態:dp[i][0/1]代表代表i節點與它的子樹以及連向父親的那一條邊的最小的Val,j=1為父親放燈,j=0代表父親不放燈。
考慮兩種方案:
1.  i放燈:i放燈的話,對於其他的沒有什么要求,所以dp[i][j]+=dp[son][1],dp[i][j]+=Eps。如果當前j==0,並且不是根節點,那么dp[i][j]++,因為到父親的那一條邊只有1個燈。
2.  i不放燈:i不放燈,轉移就有限制條件了,必須父親放燈,或者i為根節點,dp[i][1]+=dp[son][0],如果i不是根節點,那么還要++,同樣的因為到父親的那一條邊只有1個燈。
  然后一邊dfs一邊DP就可以了。注意狀態轉移是錯綜復雜的,並不是單一的0->1或0->0,具體順序見代碼。
  條件1可以更新j=1和0的情況;條件2只能更新j=1的情況,但是在根節點也可以更新j=0的情況。

 1 #include<queue>
 2 #include<cstdio>
 3 #include<vector>
 4 #include<cstring>
 5 #include<iostream>
 6 #include<algorithm>
 7 #define RG register
 8 #define LL long long
 9 #define fre(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout);
10 using namespace std;
11 const int MAXN=5000,Eps=2000;
12 int n,num,m,Case,ans;
13 int dp[MAXN][2];
14 int head[MAXN],to[MAXN],Next[MAXN];
15 bool vis[MAXN];
16 void dfs(int u,int fa)
17 {
18    vis[u]=1;
19    int sum1=0,sum2=Eps;
20    for(int i=head[u];i;i=Next[i])
21       {
22          int v=to[i];
23          if(v==fa)continue;
24          dfs(v,u);
25          sum1+=dp[v][0];//不放燈
26          sum2+=dp[v][1];//放燈
27       }
28    if(fa!=0)sum1++;
29    dp[u][1]=sum1;
30    dp[u][1]=min(dp[u][1],sum2);//與放燈的再比較一下。
31    dp[u][0]=sum2;
32    if(fa!=0) dp[u][0]++;
33    if(fa==0)
34       dp[u][0]=min(dp[u][0],sum1);
35 }
36 void add(int f,int t)
37 {
38    Next[++num]=head[f];
39    to[num]=t;
40    head[f]=num;
41 }
42 int main()
43 {
44    scanf("%d",&Case);
45    while(Case--)
46       {
47          scanf("%d%d",&n,&m);
48          num=0;
49          memset(head,0,sizeof head);
50          memset(vis,0,sizeof vis);
51          memset(dp,0,sizeof dp);
52          for(int i=1,a,b;i<=m;i++)
53             {
54                scanf("%d%d",&a,&b);
55                a++,b++;
56                add(a,b); add(b,a);
57             }
58          ans=0;
59          for(int i=1;i<=n;i++)
60             if(!vis[i])
61                {
62                   dfs(i,0);
63                   ans+=min(dp[i][0],dp[i][1]);
64                }
65          printf("%d %d %d\n",ans/Eps,m-ans%Eps,ans%Eps);
66       }
67    return 0;
68 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM