Newcoder 110 E.Pocky游戏(状压DP)

本文介绍了一种社交活动中男孩和女孩进行的pockypockypocky传递游戏,旨在通过算法找出团队难度的最小可能总和。游戏涉及两队间的竞争,其中一方需计算最优身高分配策略以降低传递难度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

社交活动中有nnn个男孩和nnn个女孩。

他们玩的其中一场比赛是通过pockypockypocky传递橡皮筋(pockypockypocky是一种长条饼干)。

与正常的社交活动游戏不同,只有两支队伍,男队和女队。

游戏规则如下,

1.两队都有nnn个编号从111nnn的马甲。

2.对手队将给出长度为mmm的整数序列a1,...,ama_1,...,a_ma1,...,am。所有数字将在111nnn(含nnn)之间,并且所有相邻数字都不相同(对于所有1≤i&lt;m,ai≠ai+11\le i&lt;m,a_i\neq a_{i+1}1i<mai̸=ai+1)。

3.在对手队给出序列之后,另一队开始计划背心的穿着策略。请注意,团队成员在游戏过程中不能交换背心。

4.当比赛开始时,橡皮筋将被放在穿着a1a_1a1编号的背心的队员嘴中的pockypockypocky上。 然后,穿着a1a_1a1编号的背心的队员必须将橡皮筋传递给穿着a2a_2a2编号的背心的队员,而不能用手,如此进行下去…。当橡皮筋通过m−1m - 1m1次并且已经在穿着ama_mam编号的背心的队员的pockypockypocky上时,游戏将结束。

5.第一队完成是胜利的队。

ShikyShikyShiky是算法大师。他从未在任何战略游戏中失利。他相信他会像以前一样赢得这场比赛。

他发现两名队员之间的身高差距越大,他们之间的橡皮筋越难通过。

基于这一观察,他定义了一次传递橡皮筋的难度为身高差的绝对值。

你是ShikyShikyShiky对手队伍的领导者。

你知道ShikyShikyShiky编写了一个程序来计算他的团队难度的最小可能总和。

既然你不想输,你决定为你的团队编写一个具有相同功能的程序。

PS :由于ShikyShikyShiky有女朋友,所以你不能引诱他获取源代码,而且他女朋友没参加这场比赛(233333323333332333333

Input

第一行有两个整数nnnmmm。 它表明有nnn个男孩和nnn个女孩,而ShikyShikyShiky的团队会给你一个长度为mmm的序列。

第二行有nnn个整数h1,h2...,hnh_1,h_2 ...,h_nh1h2...hn,表示你所有团队成员的身高。

第三行有mmm个整数a1,a2...,ama_1,a_2 ...,a_ma1a2...am,表示ShikyShikyShiky团队给出的序列。

(1≤n≤20,2≤m≤1000,120≤hi≤240,1≤ai≤n,ai≠ai+1)(1\le n\le 20,2\le m\le 1000,120\le h_i\le 240,1\le a_i\le n,a_i\neq a_{i+1})(1n20,2m1000,120hi240,1ain,ai̸=ai+1)

Output

输出一个整数表示团队难度的最小可能总和。

Sample Input

3 3
170 175 180
1 2 3

Sample Output

10

Solution

状压DPDPDP,从低到高安排身高,以dp[S]dp[S]dp[S]表示已经给SSS状态的人安排好了前num[S]num[S]num[S]个身高,其中num[S]num[S]num[S]表示SSS二进制表示中111的个数,对于SSS中为111的位置xxx,考虑给xxx身高为hnum[S]h_{num[S]}hnum[S],那么把xxx安排进去之后,对团队难度的贡献分为两部分:正部分为xxxSSS中为111位置yyy的相邻次数,因为yyy的身高必然小于xxx,而负部分为xxxSSS中为000位置yyy的相邻次数,因为yyy的身高必然大于xxx,记num[x][y]num[x][y]num[x][y]a1,...,ama_1,...,a_ma1,...,amx,yx,yx,y相邻的次数,那么即可O(n)O(n)O(n)统计该贡献,记为zzz,进而有转移
dp[S]=min(dp[S],dp[S−2x]+z⋅hnum[S]) dp[S]=min(dp[S],dp[S-2^x]+z\cdot h_{num[S]}) dp[S]=min(dp[S],dp[S2x]+zhnum[S])
dp[2n−1]dp[2^n-1]dp[2n1]记为答案,时间复杂度O(n2⋅2n)O(n^2\cdot 2^n)O(n22n)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f,maxn=(1<<20)+5;
int n,m,h[22],a[1005],cnt[22][22],dp[maxn],num[maxn];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&h[i]);
	sort(h+1,h+n+1);
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&a[i]);
		a[i]--;
		if(i>1)cnt[a[i]][a[i-1]]++,cnt[a[i-1]][a[i]]++;
	}
	int N=1<<n;
	num[0]=0;
	for(int i=1;i<N;i++)num[i]=num[i/2]+(i&1);
	for(int i=1;i<N;i++)dp[i]=INF;
	dp[0]=0; 
	for(int S=1;S<N;S++)
		for(int x=0;x<n;x++)
			if((S>>x)&1)
			{
				int y=0;
				for(int i=0;i<n;i++)
					if((S>>i)&1)y+=cnt[i][x];
					else y-=cnt[i][x];
				dp[S]=min(dp[S],dp[S^(1<<x)]+y*h[num[S]]);
			}
	printf("%d\n",dp[N-1]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值