状态DP -- HiHoCoder 1170 Robots

#1170 : Robots

Time Limit: 2000ms
Case Time Limit: 1000ms
Memory Limit: 256MB

Description

XiaoIce has N brothers who are also robots. The robots are now standing in a line. Each of them is assigned with a color. XiaoIce wants them to rearrange their positions so that robots with the same color stand together. In other words, any robot between any two robots with the same color is also assigned with that color.

Provided that only adjacent robots can be swapped, what is the minimum number of swappings to achieve the goal?

Input

The first line contains an integer T, indicating the number of test cases.

Each test case contains two lines.

The first line contains two integers N and K, indicating the number of robots and the number of distinct colors.

The second line contains N integers, and the ith number is the color of the ith robot ranging from 1 to K inclusively.

Output

For each test case, output a line containing "Case #X: Y". X is the test case number starting from 1, and Y is the answer.

Limits

1 ≤ T ≤ 20

1 ≤ N ≤ 105

Small

1 ≤ K ≤ 3

Large

1 ≤ K ≤ 16

Sample Input
3
4 2
1 2 1 2
6 4
2 1 4 3 1 2
8 6
1 3 2 5 5 4 5 2
Sample Output
Case #1: 1
Case #2: 6
Case #3: 5

        初次看这道题,“最少”让我想到了贪心、网络流和DP,“1 ≤ N ≤ 105”让我想到了贪心和线性DP,“1 ≤ K ≤ 16”让我想到了搜索和状态DP(一般看到某个数据小于等于20,题目还是让求最优或者是方案数的都要往状态DP那里考虑一下。命中的概率还是不小的)


        由于我这个人特别懒,所以我先考虑了贪心的方法。但是这道题里根本抽不出来小的情形,没办法进行局部贪心。各个数之间都是相关联的,自然也没办法从前往后撸一遍。所以贪心和线性DP被枪毙了。至于搜索和网络流那简直就是在扯淡。


        真不开心。。。。。又是状态DP题。好吧,虽然无比蛋疼但是还是要坦然面对。对于这道状态DP,那就直接定义dp[1<<k](说的好像还能有别的定义方法似的 = 皿 = )。中间的数作为状态,化为二进制后如果某一位是1,则表示这种颜色必须被安排好。如果是0则表示这种颜色被忽略,不做考虑。最终的答案当然就是dp[(1<<k)-1]啦。


        我们可以很容易的知道如何进行转移。对于当前要求出来的dp[x],枚举一种处在x内的颜色m,然后看dp[x - (1 << m)]要如何转移到dp[x].


        虽然大家都知道DP当然是要满足无后效性的,但是在考虑的情况比较多的时候还是有可能弄混乱。


        言归正传,dp[x - (1 << m)] 转移到dp[x]一定是要加上一个额外的值的。这个值就是    为了把颜色m从x的状态中抽出来并组成一段连续的序列所需要的总步数tt。


        那么我们把tt解决掉就可以了。


          tt要如何做呢?       只要枚举x中包含的其他的颜色,然后看为了把颜色j从这些颜色中剥离开来用到的总次数就行了。总次数就是每个颜色代码后面的j颜色代码的数量和。


        说起来拗口了一点,直接看代码吧。


       

#include<iostream>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<climits>
#define inf LONG_LONG_MAX
using namespace std;

int pre[20][20],x[20];
long long int dp[(1<<17) + 5];
int T,n,k;

void init()
{
	memset(tot,0,sizeof(tot));
	memset(pre,0,sizeof(pre));
	memset(dp,0,sizeof(dp));
	cin>>n>>k;
	for(int i = 1;i<=n;i++)
	{
		int t;
		cin>>t;
		t--;
		for(int j = 0;j<k;j++)
			pre[j][t] += x[j];
		x[t]++;
	}
}

int main()
{
	cin>>T;
	for(int zxc = 1;zxc<=T;zxc++)
	{
		init();
		dp[0] = 0;
		dp[0] = 0;
		for (int i = 1; i < (1 << k); i++) 
		{
			dp[i] = 1LL * n * n;
			for (int j = 0; j < k; j++)
			if (i & (1 << j)) 
			{
				long long tt = 0;
				for (int p = 0; p < k; p++)
				if ((i & (1 << p)) && p != j) 
				tt += pre[p][j];
				dp[i] = min(dp[i], dp[i - (1 << j)] + tt);
			}
		}
		printf("Case #%d: %lld\n", zxc, dp[(1 << k) - 1]);
	}
	return 0;
}

        我的语言表达能力还是弱了点。如果没有看懂的话,欢迎留言讨论!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值