树的边覆盖与树的点覆盖

        对于一条边要将其覆盖需要选这条边的父亲节点或者儿子节点其中一个即可,对于一个点的覆盖,同样可以选父亲节点或者儿子节点,但同时可以选自己这个节点。所以前者只有两种转移,后者有三种转移。

 边覆盖题目链接

        思路:

        用0表示当前节点不选,那么要想覆盖这条边,儿子节点就一定要选。用1表示当前节点选,那么儿子节点就可以不选。

        代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
#define fi first
#define se second
const ll mod=1e9+7;
const int N=2e5+10;
#define int ll
const double eps=1e-6;

ll qpow(ll a,ll b){
	ll res=1;
	while(b){
		if(b&1) res=(res*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return res;
}

int n;
vector<int> v[N];
int dp[N][2];

void dfs(int x,int f){
	dp[x][0]=0,dp[x][1]=1;
	for(auto y:v[x]){
		if(y==f) continue;
		dfs(y,x);
		dp[x][0]+=dp[y][1];
		dp[x][1]+=min(dp[y][0],dp[y][1]);
	}
}

void solve(){
	while(cin>>n){
		for(int i=0;i<=n;i++){
			v[i].clear();
		}
		for(int i=1;i<=n;i++){
			int x,num; char t;
            cin >> x >> t >> t >> num >> t;
			while(num--){
				int y;cin>>y;
				v[x].push_back(y);
				v[y].push_back(x);
			}
		}
		dfs(0,0);
		cout<<min(dp[0][0],dp[0][1])<<endl;
	}
	
}

signed main(){
	//ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
	while (t--){
		solve();
	}
	return 0;
}

点覆盖题目链接

        思路:

        用0表示当前节点被选,1表示任意儿子节点被选且当前节点不被选,2表示父亲节点被选且当前节点不被选。

        如果是0,那么可以从儿子节点的0,1,2状态转移(转移来源要有2的话,表示当前节点一定要选,因为当前节点就是儿子节点的父亲节点)。

        如果是1的话,我们要从当前节点的所有儿子节点里去找令哪些儿子节点被选。由于我们必须要一个儿子节点被选,也就是说选的是儿子节点的0状态,又由于儿子节点除了必须要选的点,其他点可以不被选,也就是1状态(父亲节点也就是当前节点不选,本身又不选,只剩1状态了),我们可以先将所有儿子节点的min(dp0,dp1)都加上,然后再判断这些min里面有没有0状态,如果有我们就不用再改变儿子节点的状态,如果没有我们要找一个儿子节点转换成0状态,也就是去找所有儿子节点里dp0-dp1最小的(因为没有一个0状态也就是说所有儿子节点的dp0都比dp1大,那么我们贪心的选需要增加的dp0-dp1最小即可)。

        如果是2的话,从0,1转移,因为当前节点没有被选,也就是儿子节点的父亲节点没有被选,不能从2(父亲节点被选)转移。

        代码:

# include<bits/stdc++.h>
using namespace std;
int n;
int f[10000+10][3];
vector<int> ve[10000+10];
void dfs(int x, int fa)
{
    f[x][0] = 1;
    f[x][1] = 0;
    f[x][2] = 0;
    int inc = 0x3f3f3f;
    for (int i=0; i<ve[x].size(); i++)
    {
        int j = ve[x][i];
        if (j == fa) continue;
        dfs(j, x);
        f[x][0] += min(f[j][1], min(f[j][0], f[j][2]));
        f[x][2] += min(f[j][0], f[j][1]);
        f[x][1] += min(f[j][0], f[j][1]);
        inc = min(f[j][0]-f[j][1], inc);
    }
    if (inc<0)
        inc = 0;
    f[x][1] += inc;
}
int main()
{
    cin>>n;
    int x, y;
    while (cin>>x>>y)
    {    
        ve[x].push_back(y);
        ve[y].push_back(x);   
    }
    dfs(1, 0);
    cout<<min(f[1][0], f[1][1])<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值