[ABC318D] General Weighted Max Matching 题解

前置

赛时卡了我50分钟,而E题只花了我6分钟

题意简述

给定一个有 n n n 个点的无向完全图,并给定每两点之间边的权值,求在满足以下条件时,选择的边的权值之和最大为多少:

∙ \bullet 所选的边的端点两两不同(即,每个点只能作为某一条被选择的边的端点)。

解题思路

不难发现最多只能取 n / 2 n/2 n/2 条边,否则必有点在选择的边中出现大于一次。又观察到 n ≤ 16 n \leq 16 n16 ,于是考虑状压dp。对于每一个状态,可以 n 2 n^2 n2 枚举它是由哪一个状态转移而来的。举个例子:

n = 6 n=6 n=6 时,对于状态 110011,可以由以下状态转移而来: 110000 100001 000011 010001 100010 010010

也就是说,由当前状态去掉两个 1的状态转移而来。去掉的这两个 1就表示当前枚举的这条边连接点 x , y x,y x,y ,其中 x , y x,y x,y 分别为两个 1从右往左数的位置。我的做法是用一个数组记录当前状态中每个 1的位置,然后 n 2 n^2 n2 枚举。总复杂度 O ( 2 n × n 2 ) O(2^n \times n^2) O(2n×n2)

代码示例

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,d[20][20],ans=0,dp[10000010];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++) cin>>d[i][j];
	}
	for(int i=0;i<(1<<n);i++){
		int O[20],cnt=0,num=i,p=0,K[20];
		while(num>0){
			K[++cnt]=num%2;
			num/=2;
		}
        //将当前状态转化为二进制
		for(int i=1;i<=cnt;i++) if(K[i]==1) O[++p]=i;
        //找到1的位置并记录
		if(cnt<2) continue;
        //没有1或只有1个1显然无法转移
		for(int x=1;x<=p;x++){
			for(int y=x+1;y<=p;y++){
            //枚举当前边连接点x和点y
				int X=i;
				X&=(~(1<<(O[x]-1)));
				X&=(~(1<<(O[y]-1)));
                //得到转移前的状态
				dp[i]=max(dp[i],dp[X]+d[O[x]][O[y]]);
                //转移
			}
		} 
		ans=max(ans,dp[i]);
        //取ans
	}
	cout<<ans<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值