hihocoder #1170 机器人 && 编程之美2015复赛

题意:

时间限制: 2000ms
单点时限: 1000ms
内存限制: 256MB

描述

小冰的N个机器人兄弟排成一列,每个机器人有一个颜色。现在小冰想让同一颜色的机器人聚在一起,即任意两个同颜色的机器人之间没有其他颜色的的机器人。

假设任意相邻的两个机器人可以交换位置,请问最少需要多少次交换?

输入

第一行为一个整数T,为数据组数,之后每组数据两行。

第一行为N和K,表示机器人的个数与颜色的总数。

接下来一行N个数,第i个数表示第i个机器人的颜色,取值范围为1到K。

输出

对于每组数据输出一行,形如"Case #X: Y"。X为数据组数,从1开始,Y为最少的交换步数。

数据范围

1 ≤ T ≤ 20

1 ≤ N ≤ 105

小数据

1 ≤ K ≤ 3

大数据

1 ≤ K ≤ 16

题解:

1.状态压缩dp,dp[11001]表示1,2,5这三种机器人已经达到要求(并且1,2,5排在3,4号机器人的前面)所需要的最

少步数

2.举个例子说明:1,2,3,4,5这五种机器人,当dp[10001]转移到dp[11001],要想让2号达到要求,并且已知1,5号机器人

已经达到要求,就需要让2,3,4号机器人通过交换位置使得,2号机器人排在一起,且处于3,4号机器人的前边

3.达到这个目标,需要知道:逆序数对==通过交换最少的次数达到'有序'

4.预处理i号机器人(范围[1,k])到j号机器人(范围[1,k])所产生的逆序对数pre[i][j]

5.最后达到3的目标,pre[2][3] + pre[2][4]就是将2号机器人换到3,4号机器人前方所需要的步数

总结:

1.开始想用贪心写,写完之后才发现错了

2.看到网上说状态压缩,其实就是暴力,想了好多天依旧没有想出来

3.看到逆序对数==最少交换步数才差不多想出来怎么做的

4.感觉还需要努力,加强全面思考问题的能力

5.最近在学习图像处理,opencv,进度太慢被老师说了,需要提高工作效率!

6.项目这种事情还是要快,快,快,不能像ACM享受慢慢思考并弄懂问题的快乐,也可能与平时效率低下不愿面对自

己的问题有关

7.总之,不同的科目有不同的学习方法,但是不管什么都需要提高效率,并且都要弄懂原理

8.不纠结于细节,快速找到自己需要学习的目标。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
typedef long long LL;
#define MAXN 100005
#define INF 0x3f3f3f3f3f3f3f3f
#define MAXM 16
#define MAXP 65536
int n,m,pre[MAXM][MAXM];
LL dp[MAXP];
vector<int>vec[MAXM];
int main()
{
    int _,cur;
    for(int kcas = scanf("%d",&_);kcas <= _;kcas++)
    {
        scanf("%d%d",&n,&m);
        for(int i = 0;i < m;i++)vec[i].clear();
        for(int i = 0;i < n;i++)
        {
            scanf("%d",&cur);
            vec[cur - 1].push_back(i);
        }
        memset(pre,0,sizeof(pre));
        for(int i = 0;i < m;i++)
            for(int j = 0;j < m;j++)if(i != j)
                for(int u = 0,v = 0;u < vec[i].size();u++)
                {
                    while(v < vec[j].size() && vec[i][u] > vec[j][v])
                        v++;
                    pre[i][j] += v;
                }
        memset(dp,0x3f,sizeof(dp));
        dp[0] = 0;
        int cnt = 1 << m;
        for(int i = 0;i < cnt;i++)
        {
            for(int j = 0;j < m;j++)if(!(i >> j & 1))
            {
                LL num = 0;
                for(int k = 0;k < m;k++)if(!(i >> k & 1) && k != j)
                    num += pre[j][k];
                dp[i | (1 << j)] = min(dp[i | (1 << j)],dp[i] + num);
            }
        }
        printf("Case #%d: %lld\n",kcas,dp[(1 << m) - 1]);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值