【bzoj2665】【CQOI2012】【编号】【dp】

本文介绍了一种生成不冲突十六进制编号的算法,确保任意两编号至少三位置不同。通过保存五个关键位置并利用21种排列方式,实现暴力枚举以找到第k小的编号。

Description

你需要给一批商品编号,其中每个编号都是一个7位16进制数(由0~9, a-f组成)。为了防止在人工处理时不小心把编号弄错,要求任意两个编号至少有三个位置对应的数字不相同。第一个编号为0000000,第二个编号为不违反上述规定的前提下最小的编号,…,每次分配一个新编号时,总是选择不和前面编号冲突的最小编号(注意编号都是16进制数,可以比较大小)。按此规律,前面若干编号分别是:0000000, 0000111, 0000222, …, 0000fff, 0001012, 0001103,0001230,0001321,0001456,…
输入k,你的任务是求出第k小的编号。
 

Input

第一行为整数k
 

Output

 
输出第k小的编号(字母必须输出小写)。输入保证这个编号存在。

Sample Input

20

Sample Output

0001321

HINT


编号


1-3


4-7


8-10


k


<=200


<=10000


<=200000

题解:
       可以发现其实只需要保存5个位置.
       因为至少有两个位置的数不同,如果这两个位置不同,那至少有三个位置不同.
       从7个数中选5个数的方案有21种.
       设f[x][i][j][k][l][a][b][c]表示第x种排列5个数分别是i,j,k,l,a,b,c;
       暴力枚举即可。
#include<iostream>
#include<cstdio>
#include<cstring>
int n;
bool f[22][20][20][20][20][20];
using namespace std;
void out(int i,int j,int k,int l,int a,int b,int c){
   if (i<10) cout<<i;else cout<<char(i-10+(int)'a');
   if (j<10) cout<<j;else cout<<char(j-10+(int)'a');
   if (k<10) cout<<k;else cout<<char(k-10+(int)'a');
   if (l<10) cout<<l;else cout<<char(l-10+(int)'a');
   if (a<10) cout<<a;else cout<<char(a-10+(int)'a');
   if (b<10) cout<<b;else cout<<char(b-10+(int)'a');
   if (c<10) cout<<c;else cout<<char(c-10+(int)'a');
} 
int main(){
  scanf("%d",&n);
  for (int i=0;i<=15;i++)
    for (int j=0;j<=15;j++)
      for (int k=0;k<=15;k++)
        for (int l=0;l<=15;l++)
          for (int a=0;a<=15;a++)
            for (int b=0;b<=15;b++)
              for (int c=0;c<=15;c++)
                if (!f[1][k][l][a][b][c]&&!f[2][j][l][a][b][c]&&
				    !f[3][j][k][a][b][c]&&!f[4][j][k][l][b][c]&&
				    !f[5][j][k][l][a][c]&&!f[6][j][k][l][a][b]&&
				    !f[7][i][l][a][b][c]&&!f[8][i][k][a][b][c]&&
					!f[9][i][k][l][b][c]&&!f[10][i][k][l][a][c]&&
					!f[11][i][k][l][a][b]&&!f[12][i][j][a][b][c]&&
					!f[13][i][j][l][b][c]&&!f[14][i][j][l][a][c]&&
					!f[15][i][j][l][a][b]&&!f[16][i][j][k][b][c]&&
					!f[17][i][j][k][a][c]&&!f[18][i][j][k][a][b]&&
					!f[19][i][j][k][l][c]&&!f[20][i][j][k][l][b]&&
					!f[21][i][j][k][l][a])
			   {
			     n--;
			     if (!n){
			       out(i,j,k,l,a,b,c);
			       return 0;
			     }
			   f[1][k][l][a][b][c]=1;f[2][j][l][a][b][c]=1;
			   f[3][j][k][a][b][c]=1;f[4][j][k][l][b][c]=1;
			   f[5][j][k][l][a][c]=1;f[6][j][k][l][a][b]=1;
			   f[7][i][l][a][b][c]=1;f[8][i][k][a][b][c]=1;
			   f[9][i][k][l][b][c]=1;f[10][i][k][l][a][c]=1;
			   f[11][i][k][l][a][b]=1;f[12][i][j][a][b][c]=1;
			   f[13][i][j][l][b][c]=1;f[14][i][j][l][a][c]=1;
			   f[15][i][j][l][a][b]=1;f[16][i][j][k][b][c]=1;
			   f[17][i][j][k][a][c]=1;f[18][i][j][k][a][b]=1;
			   f[19][i][j][k][l][c]=1;f[20][i][j][k][l][b]=1;
			   f[21][i][j][k][l][a]=1;	
			 }				
}

      
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值