• uva10859


    一道多目标优化题。

    一个是要求要在尽量小的节点上放灯,使得所有边都被照亮,这是优化一个最小值,并且这个最小值的优化的优先级最大。

    另一个是要求在灯的总数最小的前提下,被两盏灯同时照亮的边数应该尽量大,这是一个最大值的优化。

    白书上说,我们可以把这个一个是最小值,一个是最大值的优化改变成两个最小值的优化。

    方法是把“被两盏灯同时照亮的边数应该尽量大”的优化改成恰好被一盏灯照亮的边数的最小值。(这两者是等价的)

    这样就把问题转变成了两个最小值的优化。

    今天新学的trick是以一个变量来同时优化这两个最小值。

    令放置的灯数为a,恰好被一盏灯照亮的边数为c,且令M为一个大整数。

    令x = a*M + c即可,我们只需要优化x最小。

    这么做的道理在哪呢?

    首先声明M是一个非常大的整数,以至于x的值主要是由a来决定而不是c。

    这样就保证了以a作为前提,a的优先级最大。

    当a相同时,x的值自然就取决于c。(这有点像两个关键字的排序问题)

    所以M应该不受c的影响,具体而言,M是一个比“c的最大理论值与最小理论值之差”还要大的数,本题中,我们取M = 2000。

    并且本题说明这是一个“无向无环图”,也就是树,我们自然想到树形dp。

    考虑到当前的节点放灯与不放灯对后续的节点有影响,所以我们设状态的时候要多设一位表示该节点放不放灯。

    设d(i,j)表示以i为根的子树(包括i)在i的父亲放不放灯的情况下的x的最小值。(j=0:不放;j=1:放)

    决策1:当前节点不放灯。这个决策只能它的父亲节点放了灯或者当前节点就是根节点才能被作出。

    d(i,j) = sum{d(son,0)}。

    如果当前节点不是根,d(i,j)还要+1。因为此时当前节点与其父亲的连边上只被一盏灯照亮。

    决策2:当前节点放灯。当该节点的父亲节点没有放灯时,该节点一定要放灯。(也就是说不能做决策1)

    d(i,j) = sum{d(son,1)} + M。

    如果父亲节点没有放灯,且当前节点不是根,d(i,j)还要+1。

    两个决策取能做的决策中的最小值。

    代码部分模仿lrj白书,有修改。

    #include <cstdio>
    #include <cstring>
    #include <algorithm> 
    
    using namespace std;
    
    const int maxn = 1005, maxm = maxn * 2, M = 2000;
    
    int t, n, m, tot;
    
    int h[maxn], vis[maxn][2], d[maxn][2], isroot[maxn];
    
    struct edge
    {
        int v, next;
    }a[maxm];
    
    void add(int x, int y)
    {
        a[tot].v = y;
        a[tot].next = h[x];
        h[x] = tot++;
    }
    
    int dfs(int u, int f, int j)
    {
        if (vis[u][j]) return d[u][j];
        vis[u][j] = 1;
        int& ans = d[u][j];
        int s1 = 0, s2 = 0;//s1不放灯,s2放灯。 
        for (int i = h[u]; ~i; i = a[i].next)
        {
            int v = a[i].v;
            if (v == f) continue;
            if (j == 1 || isroot[u])
            {
                s1 += dfs(v, u, 0);
            }
            s2 += dfs(v, u, 1);
        }
        if (!isroot[u]) s1++;
        s2 += M;
        if (j == 0 && !isroot[u]) s2++;
        if (j == 0 && !isroot[u]) return ans = s2;//父亲没放灯,当前结点一定要放灯。 
        return ans = min(s1, s2);
    }
    
    void solve()
    {
        scanf("%d%d", &n, &m);
        memset(h, -1, sizeof h); tot = 0;
        for (int i = 0; i < m; i++)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            add(x, y); add(y, x);
        }
        memset(vis, 0, sizeof vis);
        memset(isroot, 0, sizeof isroot);
        int ans = 0;
        for (int i = 0; i < n; i++)
                if (!vis[i][0])
                {
                    isroot[i] = 1;
                    ans += dfs(i, -1, 0);
                }
        printf("%d %d %d
    ", ans / 2000, m - ans % 2000, ans % 2000);//最后还原a,c
    }
    
    int main()
    {
    //    freopen("uva10859.in", "r", stdin); 
        scanf("%d", &t);
        while (t--) solve();
        return 0;
    }
  • 相关阅读:
    捕获控制台输出信息
    .net 值类型变量赋值需要注意
    《河北省实施〈中华人民共和国道路交通安全法〉办法》
    在Win7应用程序中使用盾牌图标
    32位WIndows 7的“快速启动”,重启后丢失的问题,被确认是一个bug
    在本地网页中,让flash可以访问本地文件
    WPF 3D编程中,摄像机的位置很重要
    让Windows7任务栏图标显示Windows菜单(恢复,最小化,关闭)
    Joomla 书籍
    Joomla! 1.5 Template Toolbar简化Joomla模板做成的工具
  • 原文地址:https://www.cnblogs.com/yohanlong/p/7764014.html
Copyright © 2020-2023  润新知