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