gym:Problem C Card Hand Sorting(状态枚举+最长上升子序列)(用插入的方法从未知串到已知串的最少操作次数)

			20162017-acmicpc-nordic-collegiate-programming-contest-ncpc-2016

Problem C Card Hand Sorting

题目链接 http://codeforces.com/gym/101550/attachments

题意:现在有一副牌,牌上有花色和大小,现在你需要将有相同花色的牌放在一起,同一个花色的牌必须有序排列(升序和降序都行,不同花色排序方式也可以不同),你在整理牌的时候一次操作只能把一张牌拿出来然后插在一个位置上。现在你需要输出最少的操作次数。

解题心得:

  • 因为只有四种花色52张牌,每种花色只有两种状态(升序/降序),所以可以直接将所有的状态枚举出来,创造出一个目标排列,然后考虑将现在的牌变成目标排列需要的最少操作是多少,然后对所有的结果取min就行了。
  • 最少的操作想了一下,其实就是将目标序列看做一个上升序列,然后在题目给出的序列中找一个最长上升子序列,然后将不是最长上升子序列的元素插入就行了。其实很容易明白,每次插入之后都会破坏顺序,那么怎么插最优,那肯定是往最长上升子序列中插次数最少。



#include <bits/stdc++.h>

using namespace std;
const int maxn = 2e6 + 100;

int arr[4] = {1, 2, 3, 4};
int state = (1 << 5);

struct Suit {
    vector<int> ve;
    int cnt;
} g[6];//储存每个花色的所有牌和牌的张数

vector <int> pre;

map<char, int> maps, group;
int n;

void init() {
    //将所有的牌映射成数字
    for (int i = 2; i < 10; i++) {
        maps[i + '0'] = i;
    }
    maps['T'] = 10;
    maps['J'] = 11;
    maps['Q'] = 12;
    maps['K'] = 13;
    maps['A'] = 14;

    group['s'] = 1;
    group['h'] = 2;
    group['d'] = 3;
    group['c'] = 4;
    scanf("%d", &n);

    char s[10];
    for (int i = 1; i <= n; i++) {
        scanf("%s", s);
        g[group[s[1]]].ve.push_back(maps[s[0]] + group[s[1]] * 100);//hash一下方便车处理
        pre.push_back(maps[s[0]] + group[s[1]] * 100);
        g[group[s[1]]].cnt++;
    }
}

int longest_up(vector<int> pre, vector<int> odr) {
    vector <int> now;//假设枚举的序列是上升序列
    for(int i=0;i<n;i++) {
        int num = pre[i];
        for(int j=0;j<n;j++) {
            if(num == odr[j]) {
                now.push_back(j);
                break;
            }
        }
    }

    int tot=0;
    vector <int> dp;//找最长上升子序列
    for(int i=0;i<n;i++) {
        if(dp.size() == 0) {
            dp.push_back(now[i]);
            tot++;
            continue;
        }
        if(now[i] > dp[dp.size()-1]) {
            dp.push_back(now[i]);
            tot++;
        } else {
            int pos = int(lower_bound(dp.begin(), dp.end(), now[i]) - dp.begin());
            dp[pos] = now[i];
        }
    }

    return n - tot;
}

int get_Min(int state) {
    int ans = 0;
    vector <int> now = pre;//题目给出的顺序
    vector <int> num[5], odr;//odr是枚举每一个状态形成的顺序
    for(int i=0;i<4;i++) {
        int k = arr[i];
        num[i] = g[k].ve;
        sort(num[i].begin(), num[i].end());
        if(state&(1<<i)) {
            reverse(num[i].begin(), num[i].end());
        }
        for(int j=0;j<num[i].size();j++)
            odr.push_back(num[i][j]);
    }

    ans = longest_up(pre, odr);
    return ans;
}

void solve() {
    int Min = INT_MAX;
    do {
        for (int j = 0; j < state; j++) {//枚举状态
            int cnt = 0;
            cnt = get_Min(j);
            if(cnt < Min) Min = cnt;
        }
    } while (next_permutation(arr, arr + 4));//枚举排列

    printf("%d\n", Min);
}

int main() {
    //freopen("1.in", "r", stdin);
    init();
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值