Choreographer Problem

原题:Choreographer Problem

原题大意

艺术团中,有n个舞者,i舞者和(i-1)舞者、(i+1)舞者相似,(i-1)和(i+1)舞者并不相似。如果n> 1,那么第一个舞者只有相似的舞者,最后一个舞者只有相似的舞者,所有其他舞者都有两个相似的舞者。
舞蹈开始和结束在一个空荡荡的舞台。舞蹈舞蹈不应该是空的。每一分钟,以下变化之一发生在舞台上:

1.一个舞者(目前不在舞台上)出现;

2.一个舞者(目前在舞台上)离开舞台;

3.一个舞者代替另一个类似于他/她的舞者(基于舞者的切换-一个舞者离开舞台,而舞台上出现一个类似的舞者)
同时,舞台上不能有超过k个舞者。

现在编舞师正在考虑安排舞蹈,使得每一个不包含k个舞者的舞者都会在舞台上出现一次。你的工作是编写一个程序来帮助编舞。

输出:
+i表示i舞者出现在舞台上,-i表示i舞者离开舞台;
++i表示i舞者离开舞台,同时(i+1)舞者出现在舞台上;
–i表示i舞者离开舞台,同时(i-1)舞者出现在舞台上

算法分析

题目要我们生成组合 && 相邻的两个组合必须很接近由这
点,我们可以想到Gray码。

注意到台上的人数不能超过k。这和格雷码说好的不一样啊!
其实,如果我们把n阶格雷码中,1的个数大于k的元素删除
就会得到符合要求的序列。比较序列中相邻的两个元素,即
可得出操作。

程序代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
vector<int> vc;
int n,k;
void to_gray(ll x){
    int cnt = 0;
    ll ans = x^(x>>1);
    cnt = __builtin_popcount(ans);
  /*  while(ans){
        if(ans&1) cnt++;
        ans >>= 1;
    }*/
    if(cnt > k) return;
    return vc.push_back(x^(x>>1));
}
void cmp(int x, int y){
    int tmp = x^y;
    int plus = 0;
    int substr = 0;
    for(int i = 0; i < n; i++)
    {
        if((1<<i)&tmp){
            if((1<<i)&x) substr = i+1;
            if((1<<i)&y) plus = i+1;
        }
    }
    if(plus && !substr) printf("+%d", plus);
    if(!plus && substr) printf("-%d", substr);
    if(plus && substr)
      {  if(plus > substr)
                  printf("++%d", substr);
        else
                  printf("--%d", substr);
      }

}
int main(){
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    scanf("%d%d",&n, &k);
    for(int i = 0; i < (1<<n); i++)
        to_gray(i);

    for(int i = 1; i < vc.size(); i++)
        cmp(vc[i-1],vc[i]);
    printf("-%d", n);
    return 0;
}