Codeforces 132 E. Bits of merry old England

問題

レジスタがm個あるコンピュータを考える。
n個の出力をさせたい。
出力するためには、あらかじめレジスタにその値を代入しなければならないが、
レジスタに値xを代入するにはxの2進数で立っているビットの数に等しいコストがかかる。
(出力にはコストはかからない)


与えられたn個の値を出力するのに必要なコストの最小値および、
そのときにコンピュータに与える命令列を出力せよ。

制約条件

n≦250
m≦26

方針

UTPC2011の問題Hhttp://www.utpc.jp/2011/slides/cache.pdfの方針と同じ。


値が生存する区間を考えて、そこに重みづけをして、グラフを作り費用流を流す。
同じ値が連続する場合があるので、それはグラフ上で負閉路になるので、あらかじめ取り除いておく。


本問では、命令列を具体的に出力してやる必要がある。
値の出力とかは詳しくはソースコード参照。生きている変数をmapで覚えておく。
フローが流れているところは変数を殺さないようにする。
フローが合流したら変数を殺す。

ちゃんと解説かく気力がつきた。

ソースコード

int n,m,a[300],b[300],mul[300];

void run(){
  cin>>n>>m;
  map<int,int> prev;
  int ans=0;
  V=0;
  rep(i,n){
    cin>>a[i];
    if(i==0||a[i-1]!=a[i])b[V++]=a[i];
    else mul[V-1]++;
  }
  
  rep(i,V){
    #define C(x) __builtin_popcount(x)
    if(prev.count(b[i])){
      add_edge(prev[b[i]]+1,i,1,-C(b[i]));
    }
    if(i)add_edge(i-1,i,INF,0);
    prev[b[i]]=i;
    ans+=C(b[i]);
  }
  ans+=min_cost_flow(0,V-1,m-1);
  
  bool use[26]={};
  map<int,int> var;
  vector<string> res;
  rep(i,V){
    stringstream ss;
    if(!var[b[i]]){
      int v=0;
      for(;use[v];v++);
      var[b[i]]=v+1;
      use[v]=1;
      stringstream tt;
      tt<<(char)(v+'a')<<"="<<b[i];
      res.pb(tt.str());
    }
    ss<<"print("<<(char)('a'+var[b[i]]-1)<<")";
    res.pb(ss.str());
    rep(j,mul[i])res.pb(res.back());

    bool erase=1;
    if(i<V-1){
      rep(j,G[i+1].size()){
        edge &e=G[i+1][j];
        if(e.to>i+1&&e.cap==0)erase=0;
      }
    }
    if(erase){
      use[var[b[i]]-1]=0;
      var[b[i]]=0;
    }
  }
  cout<<res.size()<<" "<<ans<<endl;
  rep(i,res.size())cout<<res[i]<<endl;
}