【NOIP2015提高组】 Day1 T3 斗地主

【题目描述】

牛牛最近迷上了一种叫斗地主的扑克游戏。斗地主是一种使用黑桃、红心、梅花、方片的A到K加上大小王的共54张牌来进行的扑克牌游戏。在斗地主中,牌的大小关系根据牌的数码表示如下:3<4<5<6<7<8<9<10<J<Q<K<A<2<小王<大王,而花色并不对牌的大小产生影响。每一局游戏中,一副手牌由n张牌组成。游戏者每次可以根据规定的牌型进行出牌,首先打光自己的手牌一方取得游戏的胜利。

现在,牛牛只想知道,对于自己的若干组手牌,分别最少需要多少次出牌可以将它们打光。请你帮他解决这个问题。

需要注意的是,本题中游戏者每次可以出手的牌型与一般的斗地主相似而略有不同。

具体规则如下:

本题数据随机,不支持hack,要hack或强力数据请点击这里

输入输出格式

输入格式:

 

第一行包含用空格隔开的2个正整数T和n,表示手牌的组数以及每组手牌的张数。

接下来T组数据,每组数据n行,每行一个非负整数对aibi表示一张牌,其中ai示牌的数码,bi表示牌的花色,中间用空格隔开。特别的,我们用1来表示数码A,11表示数码J,12表示数码Q,13表示数码K;黑桃、红心、梅花、方片分别用1-4来表示;小王的表示方法为01,大王的表示方法为02。

 

输出格式:

 

共T行,每行一个整数,表示打光第i手牌的最少次数。

 

输入输出样例

输入样例#1:
1 8
7 4
8 4
9 1
10 4
11 1
5 1
1 4
1 1
输出样例#1:
3
输入样例#2:
1 17
12 3
4 3
2 3
5 4
10 2
3 3
12 2
0 1
1 3
10 1
6 2
12 1
11 3
5 2
12 4
2 2
7 2
输出样例#2:
6

说明

样例1说明

共有1组手牌,包含8张牌:方片7,方片8,黑桃9,方片10,黑桃J,黑桃5,方片A以及黑桃A。可以通过打单顺子(方片7,方片8,黑桃9,方片10,黑桃J),单张牌(黑桃5)以及对子牌(黑桃A以及方片A)在3次内打光。

对于不同的测试点, 我们约定手牌组数T与张数n的规模如下:

数据保证:所有的手牌都是随机生成的。

 

自测时果断打了个状压dp,其中f[i]表示当前手上牌的状态为i时需要的最少出牌次数(i的第k个二进制位表示排序后的第k张牌是否出手)。f[i]的转移比较复杂,因此代码打了200+行.....

时间复杂度为O(4*3^7),但由于f[i]转移条件判断较为耗时,转移方法较多,常数巨大!!

结果这个状压代码只拿了80分...最后四个点TLE(如果不是多组数据就A了.....)

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cmath>
  5 #define M 18
  6 #define N 14
  7 #define INF 123123123
  8 using namespace std;
  9 int n,st,f[1<<24]={0},cnt[M]={0},a[M]={0},l[M]={0},r[M]={0};
 10 
 11 bool hf(int x){
 12     bool wei1,wei2;
 13     for(int i=1;i<n;i++){
 14         wei1=x&(1<<i); wei2=x&(1<<(i-1));
 15         if(a[i]==a[i-1]&&wei2&&(!wei1)) return 0;
 16     }
 17     return 1;
 18 }
 19 int dan(int y,int x){
 20     if(l[x]==-1) return st;
 21     int k=(1<<(r[x]-l[x]+1))-1;
 22     int delta=(y>>l[x])&k;
 23     if(delta){
 24         delta=(delta<<1)&k;
 25         y=(y&((1<<l[x])-1))|(delta<<l[x])|((y>>(r[x]+1))<<(r[x]+1));
 26         return y;
 27     }
 28     return st;
 29 }
 30 int dui(int y,int x){
 31     if(l[x]==-1) return st;
 32     int k=(1<<(r[x]-l[x]+1))-1;
 33     int delta=(y>>l[x])&k;
 34     if((delta<<1)&k){
 35         delta=(delta<<2)&k;
 36         y=(y&((1<<l[x])-1))|(delta<<l[x])|((y>>(r[x]+1))<<(r[x]+1));
 37         return y;
 38     }
 39     return st;
 40 }
 41 int san(int y,int x){
 42     if(l[x]==-1) return st;
 43     int k=(1<<(r[x]-l[x]+1))-1;
 44     int delta=(y>>l[x])&k;
 45     if((delta<<2)&k){
 46         delta=(delta<<3)&k;
 47         y=(y&((1<<l[x])-1))|(delta<<l[x])|((y>>(r[x]+1))<<(r[x]+1));
 48         return y;
 49     }
 50     return st;
 51 }
 52 int zha(int y,int x){
 53     if(l[x]==-1) return st;
 54     int k=(1<<(r[x]-l[x]+1))-1;
 55     int delta=(y>>l[x])&k;
 56     if((delta<<3)&k){
 57         delta=(delta<<4)&k;
 58         y=(y&((1<<l[x])-1))|(delta<<l[x])|((y>>(r[x]+1))<<(r[x]+1));
 59         return y;
 60     }
 61     return st;
 62 }
 63 int sandaiyi(int y,int x,int dai){
 64     y=san(y,x);
 65     if(y==st) return st;
 66     return dan(y,dai);
 67 }
 68 int sandaier(int y,int x,int dai){
 69     y=san(y,x);
 70     if(y==st) return st;
 71     return dui(y,dai);
 72 }
 73 int sidaier(int y,int x,int dai1,int dai2){
 74     y=zha(y,x);
 75     if(y==st) return st;
 76     y=dan(y,dai1); 
 77     if(y==st) return st;
 78     return dan(y,dai2);
 79 }
 80 int sidaidui(int y,int x,int dai1,int dai2){
 81     y=zha(y,x);
 82     if(y==st) return st;
 83     y=dui(y,dai1); 
 84     if(y==st) return st;
 85     return dui(y,dai2);
 86 }
 87 int shunzi(int y,int ll,int rr){
 88     for(int i=ll;i<=rr;i++){
 89         y=dan(y,i);
 90         if(y==st) return st;
 91     }
 92     return y;
 93 }
 94 int liandui(int y,int ll,int rr){
 95     for(int i=ll;i<=rr;i++){
 96         y=dui(y,i);
 97         if(y==st) return st;
 98     }
 99     return y;
100 }
101 int sanshun(int y,int ll,int rr){
102     for(int i=ll;i<=rr;i++){
103         y=san(y,i);
104         if(y==st) return st;
105     }
106     return y;
107 }
108 int Main(){
109     memset(cnt,0,sizeof(cnt));
110     memset(a,0,sizeof(a)); 
111     memset(l,-1,sizeof(l)); memset(r,-1,sizeof(r));
112     for(int i=1;i<=n;i++){
113         int x,k; scanf("%d%d",&x,&k);
114         if(x<3){
115             if(x==0) x=2;
116             else if(x==2) x=1;
117             else x=0;
118         }
119         x=1+(x-3+N)%N;
120         cnt[x]++; 
121     }
122     for(int i=1,j=0;i<=N;i++){
123         bool ck=1;
124         while(cnt[i]){
125             cnt[i]--;
126             if(ck){l[i]=j; ck=0;}
127             a[j++]=i;
128         } 
129         if(!ck) r[i]=j-1;
130     }
131     st=(1<<n)-1; 
132     for(int i=st;i>=0;i--) f[i]=INF;
133     f[st]=0;
134     for(int i=st;i;i--) if(hf(i)){
135         if(i==2){
136             f[st]=0;
137         }
138         for(int j=1;j<=N;j++){
139             int y=dan(i,j);
140             if(y!=st) f[y]=min(f[y],f[i]+1);
141         }
142         for(int j=1;j<=N;j++){
143             int y=dui(i,j);
144             if(y!=st) f[y]=min(f[y],f[i]+1);
145         }
146         for(int j=1;j<=N;j++){
147             int y=san(i,j);
148             if(y!=st) f[y]=min(f[y],f[i]+1);
149         }
150         for(int j=1;j<N;j++){
151             int y=zha(i,j);
152             if(y!=st) f[y]=min(f[y],f[i]+1);
153         }
154         for(int j=1;j<=N;j++) if(l[j]!=-1&&(r[j]-l[j]>1)){
155             for(int k=1;k<=N;k++) if(l[k]!=-1){
156                 int y=sandaiyi(i,j,k);
157                 if(y!=st) f[y]=min(f[y],f[i]+1);
158             }
159         }
160         for(int j=1;j<=N;j++) if(l[j]!=-1&&(r[j]-l[j]>1)){
161             for(int k=1;k<=N;k++) if(l[k]!=-1){
162                 int y=sandaier(i,j,k);
163                 if(y!=st) f[y]=min(f[y],f[i]+1);
164             }
165         }
166         for(int j=1;j<N;j++) if(l[j]!=-1){
167             for(int k=j+4;k<=N-2;k++) if(l[k]!=-1){
168                 int y=shunzi(i,j,k);
169                 if(y!=st) f[y]=min(f[y],f[i]+1);
170             }
171         }
172         for(int j=1;j<N;j++) if(l[j]!=-1&&(r[j]-l[j]>0)){
173             for(int k=j+2;k<=N-2;k++) if(l[k]!=-1&&(r[k]-l[k]>0)){
174                 int y=liandui(i,j,k);
175                 if(y!=st) f[y]=min(f[y],f[i]+1);
176             }
177         }
178         for(int j=1;j<N;j++) if(l[j]!=-1&&(r[j]-l[j]>1)){
179             for(int k=j+1;k<=N-2;k++) if(l[k]!=-1&&(r[k]-l[k]>1)){
180                 int y=sanshun(y,j,k);
181                 if(y!=st) f[y]=min(f[y],f[i]+1);
182             }
183         }
184         for(int j=1;j<=N;j++) if(l[j]!=-1&&(r[j]-l[j]>2)){
185             for(int k=1;k<=N;k++) if(l[k]!=-1){
186                 for(int L=1;L<=N;L++) if(l[L]!=-1){
187                     int y=sidaier(i,j,k,L);
188                     if(y!=st) f[y]=min(f[y],f[i]+1);    
189                 }
190             }
191         }
192         for(int j=1;j<=N;j++) if(l[j]!=-1&&(r[j]-l[j]>2)){
193             for(int k=1;k<=N;k++) if(l[k]!=-1){
194                 for(int L=1;L<=N;L++) if(l[L]!=-1){
195                     int y=sidaidui(i,j,k,L);
196                     if(y!=st) f[y]=min(f[y],f[i]+1);    
197                 }
198             }
199         }
200     }
201     printf("%d\n",f[0]);
202 }
203 
204 int main(){
205     freopen("landlords.in","r",stdin);
206     freopen("landlords.out","w",stdout);
207     int cas; scanf("%d%d",&cas,&n);
208     while(cas--) Main();
209 }

 

 

事实证明我想多了...

直接上爆搜,暴力枚举单顺,双顺和三顺,剩下的牌直接贪心(即按四带二,四带一对,四代两对,三带一,三代二,炸弹,三条,对子,单牌的顺序出,不难看出这么出牌是最优的,即出牌次数取决于顺的选择方式)。

时间复杂度O(玄学) ,所有点本地0.01s内跑出

代码:

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define M 100
 5 #define N 14
 6 using namespace std;
 7 int n,ans=10;
 8 int a[M]={0},c[M]={0};
 9 
10 int get(){
11     c[0]=c[1]=c[2]=c[3]=c[4]=0;
12     for(int i=1;i<=N;i++) c[a[i]]++;
13     int sum=0;
14     while(c[4]&&c[2]>=2) c[4]--,c[2]-=2,sum++;
15     while(c[4]&&c[1]>=2) c[4]--,c[1]-=2,sum++;
16     while(c[4]&&c[2]) c[4]--,c[2]--,sum++;
17     while(c[3]&&c[2]) c[3]--,c[2]--,sum++;
18     while(c[3]&&c[1]) c[3]--,c[1]--,sum++;
19     return sum+c[1]+c[2]+c[3]+c[4];
20 }
21 
22 void dfs(int x){
23     if(x>=ans) return;
24     ans=min(ans,x+get());
25     for(int i=1;i<=8;i++){
26         if(a[i]==0||a[i+1]==0||a[i+2]==0||a[i+3]==0) continue;
27         for(int j=i+4;j<=12;j++) if(a[j]==0) break;
28         else{
29             for(int k=i;k<=j;k++) a[k]--;
30             dfs(x+1);
31             for(int k=i;k<=j;k++) a[k]++;
32         }
33     }
34     for(int i=1;i<=10;i++){
35         if(a[i]<2||a[i+1]<2) continue;
36         for(int j=i+2;j<=12;j++) if(a[j]<2) break;
37         else{
38             for(int k=i;k<=j;k++) a[k]-=2;
39             dfs(x+1);
40             for(int k=i;k<=j;k++) a[k]+=2;
41         }
42     }
43     for(int i=1;i<=11;i++){
44         if(a[i]<3) continue;
45         for(int j=i+1;j<=12;j++) if(a[j]<3) break;
46         else{
47             for(int k=i;k<=j;k++) a[k]-=3;
48             dfs(x+1);
49             for(int k=i;k<=j;k++) a[k]+=3;
50         }
51     }
52 }
53 
54 int main(){
55     freopen("landlords.in","r",stdin);
56     freopen("landlords.out","w",stdout);
57     int cas; cin>>cas>>n;
58     while(cas--){
59         memset(a,0,sizeof(a));
60         for(int i=1;i<=n;i++){
61             int x,k; scanf("%d%d",&x,&k);
62             if(x<3){if(x==0) x=2; else if(x==2) x=1; else x=0;}
63             x=1+(x-3+N)%N; a[x]++;
64         }
65         ans=100; dfs(0);    
66         printf("%d\n",ans);
67     }
68 }

 

 

 

转载于:https://www.cnblogs.com/xiefengze1/p/7705983.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值