Newcoder 110 E.Pocky游戏(状压)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/V5ZSQ/article/details/83111114

Description

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

他们玩的其中一场比赛是通过 p o c k y pocky 传递橡皮筋( p o c k y pocky 是一种长条饼干)。

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

游戏规则如下,

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

2.对手队将给出长度为 m m 的整数序列 a 1 , . . . , a m a_1,...,a_m 。所有数字将在 1 1 n n (含 n n )之间,并且所有相邻数字都不相同(对于所有 1 i < m a i a i + 1 1\le i<m,a_i\neq a_{i+1} )。

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

4.当比赛开始时,橡皮筋将被放在穿着 a 1 a_1 编号的背心的队员嘴中的 p o c k y pocky 上。 然后,穿着 a 1 a_1 编号的背心的队员必须将橡皮筋传递给穿着 a 2 a_2 编号的背心的队员,而不能用手,如此进行下去…。当橡皮筋通过 m 1 m - 1 次并且已经在穿着 a m a_m 编号的背心的队员的 p o c k y pocky 上时,游戏将结束。

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

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

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

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

你是 S h i k y Shiky 对手队伍的领导者。

你知道 S h i k y Shiky 编写了一个程序来计算他的团队难度的最小可能总和。

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

PS :由于 S h i k y Shiky 有女朋友,所以你不能引诱他获取源代码,而且他女朋友没参加这场比赛( 2333333 2333333

Input

第一行有两个整数 n n m m 。 它表明有 n n 个男孩和 n n 个女孩,而 S h i k y Shiky 的团队会给你一个长度为 m m 的序列。

第二行有 n n 个整数 h 1 h 2 . . . h n h_1,h_2 ...,h_n ,表示你所有团队成员的身高。

第三行有 m m 个整数 a 1 a 2 . . . a m a_1,a_2 ...,a_m ,表示 S h i k y Shiky 团队给出的序列。

( 1 n 20 , 2 m 1000 , 120 h i 240 , 1 a i n , a i a i + 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})

Output

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

Sample Input

3 3
170 175 180
1 2 3

Sample Output

10

Solution

状压 D P DP ,从低到高安排身高,以 d p [ S ] dp[S] 表示已经给 S S 状态的人安排好了前 n u m [ S ] num[S] 个身高,其中 n u m [ S ] num[S] 表示 S S 二进制表示中 1 1 的个数,对于 S S 中为 1 1 的位置 x x ,考虑给 x x 身高为 h n u m [ S ] h_{num[S]} ,那么把 x x 安排进去之后,对团队难度的贡献分为两部分:正部分为 x x S S 中为 1 1 位置 y y 的相邻次数,因为 y y 的身高必然小于 x x ,而负部分为 x x S S 中为 0 0 位置 y y 的相邻次数,因为 y y 的身高必然大于 x x ,记 n u m [ x ] [ y ] num[x][y] a 1 , . . . , a m a_1,...,a_m x , y x,y 相邻的次数,那么即可 O ( n ) O(n) 统计该贡献,记为 z z ,进而有转移
d p [ S ] = m i n ( d p [ S ] , d p [ S 2 x ] + z h n u m [ S ] ) dp[S]=min(dp[S],dp[S-2^x]+z\cdot h_{num[S]})
d p [ 2 n 1 ] dp[2^n-1] 记为答案,时间复杂度 O ( n 2 2 n ) O(n^2\cdot 2^n)

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;
}

猜你喜欢

转载自blog.csdn.net/V5ZSQ/article/details/83111114
110