//dp+bitmask 排列

11 篇文章 0 订阅
3 篇文章 0 订阅

LINK-> 牛客


链接:https://ac.nowcoder.com/acm/contest/1108/G
来源:牛客网

排列
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
Special Judge, 64bit IO Format: %lld
题目描述
Bobo 有一个长度为 n 的数列 p1,p2,…,pnp_1, p_2, \dots, p_np1​,p2​,…,pn​ 和 m 个二元组 (a1,b1),(a2,b2),…,(am,bm)(a_1, b_1), (a_2, b_2), \dots, (a_m, b_m)(a1​,b1​),(a2​,b2​),…,(am​,bm​).
他想重新排列数列 p,使得 ∑i=1m∣pai−pbi∣\sum_{i = 1}^m |p_{a_i} - p_{b_i}|∑i=1m​∣pai​​−pbi​​∣ 最小。求最小值。
输入描述:

输入文件包含多组数据,请处理到文件结束。
每组数据的第一行包含 2 个整数 n 和 m。
第二行包含 n 个整数 p1,p2,…,pnp_1, p_2, \dots, p_np1​,p2​,…,pn​.
最后 m 行的第 i 行包含 2 个整数 aia_iai​ 和 bib_ibi​.

输出描述:

对于每组数据输出 1 个整数表示所求的最小值。


在这里插入图片描述

感想吧 QAQ 最后五十分钟一直在怼的一道题,不知道为什么这题对我莫名吸引力,难得的是对题目的考察知识的感觉是对的,最后过了样例瞎交结果MLE ,开心的是个人感觉和AC代码还是长得很像的(强行很像

带注释的代码 (有错求指出,有更好的想法求分享诶~~)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[1<<20];
int p[21],mp[21],c[21];
const ll inf=1e18;
int main()
{
	int n,m;while(~scanf("%d%d",&n,&m))
	{
		
		for(int i=0;i<n;i++) scanf("%d",&p[i]);
		sort(p,p+n);int a,b;
		for(int i=0;i<=n;i++) mp[i]=0,c[i]=0;
		for(int i=0;i<m;i++)
		{
			scanf("%d%d",&a,&b );a--;b--;
		    mp[a ]++;mp[b ]++;
		    c[a]|=1<<b;//储存和a匹配的数
		    c[b]|=1<<a;
		}
		dp[0]=0ll;
		for(int i=1;i<(1<<n);i++)//i中等于1 表示这些位置有值了
		{
			dp[i]=inf;
			int now=__builtin_popcount(i)-1;//现在该放p[now]
			for(int j=0;j<n;j++)
			{
				if((i>>j)&1)//p[now]放在j这个位置
				{
					dp[i]=min(dp[i],dp[(1<<j)^i]+1ll*p[now]*((__builtin_popcount(i&c[j]))*2-mp[j]));
				}
				// 已经和j匹配到的个数-还没和j匹配的个数
				// (__builtin_popcount(i&c[j]))表示已经匹配完成的  , mp[j]表示全部含有j的序列数 
				// (__builtin_popcount(i&c[j]))*2-mp[j])就表示已经和j匹配到的-还没和j匹配的
                //这样处理的原因 
				//由于处理的是|pa-pb|要保证结果是>=0  
				//之前的sort处理保证p递增所以 
				//这时候的pa值肯定比之前的pb值大,肯定比之后处理的pb值小
				//所以这里它对于已经存在的匹配作为被减数 之后还没存在的匹配作为减数 so这里p[now] 出现的次数就是被减数-减数的次数
				//以上就是我的一点理解吧 
			}
		}
		printf("%lld\n",dp[(1<<n)-1]);
	}
}
  • 没有注释的简洁版本
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[1<<20];int c[21],mp[21],p[21];
const ll inf=1e18;
int main()
{
	int n,m,a,b;while(~scanf("%d%d",&n,&m))
	{
		for(int i=0;i<n;i++) scanf("%d",&p[i]);sort(p,p+n);
		for(int i=0;i<n;i++) c[i]=0,mp[i]=0;
		for(int i=0;i<m;i++) 
		{
			scanf("%d%d",&a,&b);
			a--,b--;mp[a]++,mp[b]++;
			c[a]|=1<<b,c[b]|=1<<a;
		}
		dp[0]=0ll;
		for(int i=1;i<(1<<n);i++)
		{
			dp[i]=inf;
			int now=__builtin_popcount(i)-1;
			for(int j=0;j<n;j++)
			{
				if((i>>j)&1) 
				{
					dp[i]=min(dp[i],dp[i^(1<<j)]+1ll*p[now]*(__builtin_popcount(i&c[j])*2-mp[j]));
				}
			}
		}
		printf("%lld\n",dp[(1<<n)-1]);
	}
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值