zoukankan      html  css  js  c++  java
  • 【树形DP】【UVA10859】 Placing Lampposts

    传送门

    Description

    给定一个(n)个点(m)条边的无向无环图,选择尽量少的节点,使得所有边都至少有一个顶点被选择。在这个基础上,要求有两个顶点被选择的边数尽可能大

    Input

    多组数据。第一行是数据组数(T)

    以下(T)组,每组包括:

    第一行两个整数(n),(m)

    下面(m)行,每行两个整数(u),(v)。代表一条边。

    Output

    对于每组数据输出一行,包括三个用空格隔开的整数,分别是:

    最小的灯的个数,两个顶点都被选择的边数,一个顶点被选择的边数

    Sample Input

    2
    4 3
    0 1
    1 2
    2 3
    5 4
    0 1
    0 2
    0 3
    0 4
    

    Sample Output

    2 1 2
    1 0 4
    

    Hint

    (For~All:)
    (m~<~n~leq~1000)

    Solution

    考虑无向无环图本质上是个森林,各个树互不影响。于是下面只研究单棵树的情况。

    考虑这个题的优化目标有两个,分别是要求点数尽可能少,还有两个顶点被选择的边尽可能多。为了统一取max和min,我们将目标二改为有且仅有一个顶点被选择的边尽可能少。

    对于同时最小化两个目标,而且在第一个目标最小的时候需要最小化第二个目标的的时候,可以设第一个目标的值是(x_1),第二个目标的值是(x_2)。目标为最小化(ans=x_1~ imes~K+x_2),其中满足(K~>~max_{x_2})

    于是对于本题就可以套这种方法。由于(n~leq~1000),所以不妨设(K=2000)。设(f_{i,0/1})为以(i)为根的子树合法,且点(i)不选/选的答案。

    方程显然:

    [f_{i,0}~=~sum{f_{to,1}+1} ]

    [f_{i,1}~=~sum~min~{f_{to,1}~,~f_{to,0}+1}+k ]

    于是就没了

    Code

    #include<cstdio>
    #include<cstring>
    #define rg register
    #define ci const int
    #define cl const long long int
    
    typedef long long int ll;
    
    namespace IO {
        char buf[90];
    }
    
    template<typename T>
    inline void qr(T &x) {
        char ch=getchar(),lst=' ';
        while(ch>'9'||ch<'0') lst=ch,ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        if(lst=='-') x=-x;
    }
    
    template<typename T>
    inline void write(T x,const char aft,const bool pt) {
        if(x<0) x=-x,putchar('-');
        int top=0;
        do {
            IO::buf[++top]=x%10+'0';
            x/=10;
        } while(x);
        while(top) putchar(IO::buf[top--]);
        if(pt) putchar(aft);
    }
    
    template<typename T>
    inline T mmax(const T a,const T b) {return a > b ? a : b;}
    template<typename T>
    inline T mmin(const T a,const T b) {return a < b ? a : b;}
    template<typename T>
    inline T mabs(const T a) {return a < 0 ? -a : a;}
    
    template<typename T>
    inline void mswap(T &a,T &b) {
        T temp=a;a=b;b=temp;
    }
    
    const int st = 2000;
    const int maxn = 1010;
    const int maxm = 2010;
    
    struct Edge {
    	int to,nxt;
    };
    Edge edge[maxm];int hd[maxn],ecnt;
    inline void cont(ci from,ci to) {
    	Edge &e=edge[++ecnt];
    	e.to=to;e.nxt=hd[from];hd[from]=ecnt;
    }
    
    int n,m;
    int frog[maxn][2];
    bool vis[maxn];
    
    void clear();
    void reading();
    void dfs(ci,ci);
    
    int main() {
    	rg int t=0;qr(t);
    	while(t--) {
    		clear();
    		qr(n);qr(m);
    		rg int _ans=0;
    		reading();
    		for(rg int i=1;i<=n;++i) if(!vis[i]) {
    			dfs(i,0);_ans+=mmin(frog[i][0],frog[i][1]);
    		}
    		rg int tk=_ans%st;
    		write(_ans/st,' ',true);write(m-tk,' ',true);write(tk,'
    ',true);
    	}
    	return 0;
    }
    
    void clear() {
    	n=m=ecnt=0;
    	memset(hd,0,sizeof hd);
    	memset(vis,0,sizeof vis);
    	memset(edge,0,sizeof edge);
    	memset(frog,0,sizeof frog);
    }
    
    void reading() {
    	rg int a,b;
    	for(rg int i=1;i<=m;++i) {
    		a=b=0;qr(a);qr(b);++a,++b;
    		cont(a,b);cont(b,a);
    	}
    }
    
    void dfs(ci u,ci fa) {
    	vis[u]=true;
    	frog[u][1]=st;
    	for(rg int i=hd[u];i;i=edge[i].nxt) {
    		int &to=edge[i].to;
    		if(to == fa) continue;
    		dfs(to,u);
    		frog[u][0]+=frog[to][1]+1;
    		frog[u][1]+=mmin(frog[to][1],frog[to][0]+1);
    	}
    }
    

    Summary

    对于同时最小化两个目标,而且在第一个目标最小的时候需要最小化第二个目标的的时候,可以设第一个目标的值是(x_1),第二个目标的值是(x_2)。目标为最小化(ans=x_1~ imes~K+x_2),其中满足(K~>~max_{x_2})

  • 相关阅读:
    如何在for循环中使用多线程
    解决ios10以上H5页面手势、双击缩放问题
    select标签默认选项
    vue三级联动
    手动安装composer详细教学
    密码校验:长度6位以上,至少包含一个数字,一个大写字母,一个小写字母,不包含空格
    tp5生成6位不重复验证码
    css漂亮的阴影边框
    圆形进度条css3样式
    jQuery倒计时组件(jquery.downCount.js)
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/9760752.html
Copyright © 2011-2022 走看看