夏夜、

心若平似镜、何题不AC。

UVA 10859 Placing Lampposts 树形DP

题意:

给一个n个点m条边的无向无环图,再尽量少的结点上放上灯,使得所有边都被照亮(每个灯会照亮所有与它相连的边)

在灯的总数最小的前提下,被两盏灯照亮的边尽量大。

优化条件有两个:灯的数量a最少,被两灯照亮的边数b最大,因为边的总数是一定的,不是被一个灯照亮就是被两个灯照亮

所以第二个条件可用"被一个灯照亮的边数 c最小"代替

设x=a*M+c;M是一个足够大的常数

那么优化条件就变成只有x尽量小了,决定x大小的主要是a的值,在a的值相同的情况下才考虑c的值

最终求到x后,a=x/M,c=x%M,b=m-c;

dp[i][j]表示以结点i为跟的子树的最优解,j为父节点的放灯状态,j=0表示不放,1表示放。

则在i结点的时候有放和不放两种决策

vector<int> g[1010];
int n,m;
int dp[1001][2];
int f(int x,int st,int fa)
{
    if(dp[x][st]>=0)return dp[x][st];
    int &ans=dp[x][st];
    ans=2000;                    //在这个结点放上等的决策,任何时候都是可行的
    for(int i=0;i<g[x].size();i++)
    {
        int v=g[x][i];
        if(v!=fa)
        {
            ans+=f(v,1,x);
        }
    }
    if(st==1||fa==-1)   //不放灯的决策,当这个结点是根节点或他的父节点已经放灯
    {
        int sum=0;
        for(int i=0;i<g[x].size();i++)
        {
            int v=g[x][i];
            if(v!=fa)
            {
                sum+=f(v,0,x);
                sum++;
            }
        }
        if(fa!=-1)sum++;
        ans=min(ans,sum);
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int ca=1;ca<=t;ca++)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)g[i].clear();
        for(int i=0;i<m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            g[a].pb(b);
            g[b].pb(a);
        }
        memset(dp,-1,sizeof(dp));
        int num=0;
        for(int i=0;i<n;i++)if(dp[i][0]==-1)//可能有多课树
            num+=f(i,0,-1);
        printf("%d %d %d\n",num/2000,m-num%2000,num%2000);
    }
    return 0;
}
View Code

 

posted on 2013-08-10 11:51  BMan、  阅读(209)  评论(0编辑  收藏  举报

导航