3028: Card Hand Sorting                                                                     

这是题目链接哦

时间限制: 1 Sec  内存限制: 64 MB
提交: 76  解决: 30
[提交] [状态] [讨论版] [命题人:外部导入]

 

题目描述

NCPC2016  —— C-Card Hand Sorting(枚举子集+最长上升子序列)

When dealt cards in the card game Plump it is a good idea to start by sorting the cards in hand by suit and rank. The different suits should be grouped and the ranks should be sorted within each suit. But the order of the suits does not matter and within each suit, the cards may be sorted in either ascending or descending order on rank. It is allowed for some suits to be sorted in ascending order and others in descending order.
Sorting is done by moving one card at a time from its current position to a new position in the hand, at the start, end, or in between two adjacent cards. What is the smallest number of moves required to sort a given hand of cards?

输入

The first line of input contains an integer n (1 ≤ n ≤ 52), the number of cards in the hand. The second line contains n pairwise distinct space-separated cards, each represented by two characters. The first character of a card represents the rank and is either a digit from 2 to 9 or
one of the letters T , J , Q , K , and A representing Ten, Jack, Queen, King and Ace, respectively, given here in increasing order. The second character of a card is from the set { s , h , d , c } representing the suits spades ♠, hearts ♥, diamonds ♦, and clubs ♣.

 

输出

Output the minimum number of card moves required to sort the hand as described above.

 

样例输入

7
9d As 2s Qd 2c Jd 8h

 

样例输出

2

 

来源/分类

NCPC2016 

 

解题思路:

要求对n张牌按花色放在一起,同花色内可以按照升序或降序排列,求最小的移动次数。

首先,我们看看按照升序同花色的牌进行排序操作,那么所需要的最少移动次数便是非上升序列的牌的数量,因为要把这些牌取出插到它应该在的位置需要进行一次移动操作。

求牌的升序子序列很容易,但是如果是下降呢。emmmm 把原来的牌变成相反的大小然后再求升序,结果就是最长降序子序列啦~~~哈哈哈哈——     机智~~~(手动滑稽) 

解决完了同花色内升降序需要的最少移动步骤问题 ,那么还剩下最后一个问题——如何把不同花色分块??或者说成  如何把同花色的牌放在一块???

考虑到同一花色最高12 。。。  (这道题每种花色只有13张可以考虑用0~12表示)

直接把每种花色加个稍大些起始点    比如n*30  (0<=n<=3)。

就会出现如下情况:

第一种花色范围 [0         30)

第二种花色范围 [30       60)

第三种花色范围 [60       90)

第四种花色范围 [90       120)

每种范围放13张牌足够了!

对给出的一串乱序牌求得每个位置对应的值,当然能恰好出现上面的花色情况就是最好的,但我们的哪种花色到底应该对应哪个区间才能最大程度符合呢?答案是——暴力!略略略~~~

没错!把所有花色放在第几区域的情况都来一遍,每一遍都对不同花色升降序的情况来一遍。

总的情况数也就是 四种花色的全排列4!   *   每个花色升降序 2^4   =  384 种情况 

用二分优化的最长子序列算法很容易在短时间内跑完n =52      ....

ok  给出AC代码供参考:

  考虑n=52的最差情况 

  时间复杂度O(n*logn)*384  ≈  3.4e4  

#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<k;i++) 
using namespace std;
#define IO ios_base::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL);
//#define LOCAL
const int INF = 0x3f3f3f3f;

int n;
struct node{
	int val;
	int suit;
}card[55];

int suit[4];   //保存四种花色 0 1 2 3
int ord[4];    //保存四种花色的升降序 1升 0降
int dul[55];   //1~n张牌的逻辑信息
int seq[55];   //序列
void duel()
{
	for(int i=0;i<n;i++) 
		if(ord[card[i].suit]) dul[i] = suit[card[i].suit] * 30 +  card[i].val;  //同花色升序:按照原来的大小在自己花色区域内排序
	    else dul[i] = suit[card[i].suit] * 30  -  card[i].val + 15;   //同花色降序: 按照原来的大小在自己花色区域 以相反的顺序排序 以后求升序列长度便是其降序列长度!
}

int get_steps()
{
//     memset(seq,0x3f,sizeof(seq));  
     fill(seq,seq+n,INF); //非0、1 的数循环赋值比较快 
      for(int i= 0;i<n;i++)
      	  *std::lower_bound(seq,seq+n,dul[i]) = dul[i];  //更新上升序列 
     int len = std::lower_bound(seq,seq+n,INF) - seq;  //上升序列长度
     return n-len;    //需要移动的次数
}
int main(void)
{
	IO
	#ifdef LOCAL
	freopen("in.txt","r",stdin);
	#endif 
	cin>>n;
	char ct[2];
	rep(i,0,n) 
	{
		cin>>ct;
		if('2'<=ct[0] && ct[0]<='9') 
		  card[i].val = ct[0] - '2';  //最小从0开始  0~7
		else {
			switch(ct[0])              //8~12  每种花色共13张牌
			{
				case 'T':  card[i].val = 8; break;
				case 'J':  card[i].val = 9; break;
				case 'Q':  card[i].val = 10;break;
				case 'K':  card[i].val = 11;break;
				case 'A':  card[i].val = 12;break;
			    default:   break;  
			}
		}
		switch(t[1])          		//记录花色
		{
			case 's':  card[i].suit = 0;break;
			case 'h':  card[i].suit = 1;break;
			case 'd':  card[i].suit = 2;break;
			case 'c':  card[i].suit = 3;break;
			default :  break;
		}  
	}
	suit[0] = 0, suit[1] = 1, suit[2] = 2, suit[3] = 3;  
	int ans = INF;
	do
	{
		for(int i=1;i<(1<<4);i++)
		{
			for(int j=0;j<4;j++)
			{
				if(i&(1<<j)) ord[j] = 1;//1表示当前花色本轮为升序
				else ord[j] = 0;        //降序
				duel();                 //按照升降序、花色前后顺序,洗牌
				int stp = get_steps();  //n-最长上升序列便是需要的操作步骤数
				ans = min(ans,stp); 
			}
		}
	}while(next_permutation(suit,suit+4));   //四种花色的所有排列情况
    cout<<ans<<endl;
}

/**************************************************************
    Language: C++
    Result: 正确
    Time:0 ms
    Memory:1688 kb
****************************************************************/

  

相关文章:

猜你喜欢
  • 2021-05-18
  • 2021-06-10
  • 2021-11-09
  • 2022-01-11
  • 2022-12-23
  • 2021-12-26
  • 2022-01-06
相关资源
相似解决方案