kyos1704活動記

適当に考えたことや調べたことを垂れ流すものです。質問等ありましたらtwitter:@kyos1704 に質問してください。

ICPC 2014 Domestic

A

#include<bits/stdc++.h>
using namespace std;

int x,y,s;

bool input(){
  cin>>x>>y>>s;
  if(x==0&&y==0)return false;
  return true;
}

int pre(int a){
  for(int i=s+100;i>=0;i--){
    if(i*(x+100)/100 == a){
      return i;
    }
  }
  return 0;
}

int solve2(int a,int b){
  a = pre(a);
  b = pre(b);
  if(a==0)return 0;
  if(b==0)return 0;
  return a*(y+100)/100 + b*(y+100)/100;
}

int solve(){
  int tmp = 0;
  for(int i=0;i<s;i++){
    tmp = max(tmp,solve2(i,s-i));
  }
  return tmp;
}

int main(){
  while(input()){
    cout<<solve()<<endl;
  }

}

B
nikollsonさんが通してくれました

C
終わったあと書いたコード(正しい保証はない)
追記::climpetさんがdiffとってくれました 大丈夫っぽいそうです

#include<bits/stdc++.h>
using namespace std;

const double EPS = 1e-8;
int n,r;

struct S{
  double l,r,h;
};
vector<S> in;
vector<double> check;
void init(){
  in.clear();
  check.clear();
}
void setCheck(double x){
  check.push_back(x+2*EPS);
  check.push_back(x-2*EPS);
}
bool input(){
  cin>>r>>n;
  if(r==0)return false;
  setCheck(0);
  for(int i=0;i<n;i++){
    double l,r,h;
    cin>>l>>r>>h;
    in.push_back(S{l,r,h});
    setCheck(l);
    setCheck(r);
  }
  return true;
}

double y(double x,double t){
  if(abs(x)>r)return -1;
  return t + sqrt(r*r - x*x) - r;
}

bool checkXjudge(double x,double y){
  if(y<0)return true;
  for(int i=0;i<in.size();i++){
    if(in[i].l<x && x< in[i].r){
      if(y<=in[i].h)return true;
    }
  }
  return false;
}

bool judge(double t){
  for(int i=0;i<check.size();i++){
    if(!checkXjudge(check[i],y(check[i],t))){
      return false;
    }
  }
  return true;
}

double solve(double lt,double rt,int nn){
  if(nn==0)return lt;
  double v = (lt + rt)/2;
  if(judge(v)){
    return solve(v,rt,nn-1);
  }else{
    return solve(lt,v,nn-1);
  }
}


int main(){
  while(init(),input()){
    cout<<fixed<<setprecision(10)<<solve(0,100,100)<<endl;
    cerr<<fixed<<setprecision(10)<<solve(0,100,100)<<endl;
  }
}

D

#include<bits/stdc++.h>
using namespace std;

string s;

bool input(){
  cin>>s;
  return s!="#";
}

vector<string> ans;


string conv(string s,int n){
  for(int i=0;i<s.size();i++){
    if(n & (1<<i)){
      if(s[i] == 'z')return "#";
      s[i] = s[i]+1;
    }
  }
  return s;
}


bool judge(string s,string pre){
  for(int i=1;i<26;i++){
    for(int j=0;j<pre.size();j++){
      if(pre[j]=='a'+i){
        pre[j] = 'a'+i-1;
        break;
      }
    }
  }
  return s == pre;
}

void solve(){
  ans.clear();
  for(int i=0;i<(1<<s.size());i++){
    string tmp = conv(s,i);
    if(tmp=="#")continue;
    if(judge(s,tmp)){
      ans.push_back(tmp);
    }
  }
}

void print(){
  cout<<ans.size()<<endl;
  cerr<<ans.size()<<endl;
  sort(ans.begin(),ans.end());
  if(ans.size()<10){
    for(auto i:ans){
      cout<<i<<endl;
    }
    return ;
  }
  for(int i=0;i<5;i++){
    cout<<ans[i]<<endl;
  }
  for(int i=5;i>=1;i--){
    cout<<ans[ans.size()-i]<<endl;
  }
}

int main(){
  while(input()){
    solve();
    print();
  }
}

E

#include<bits/stdc++.h>
using namespace std;
struct S{
  long long to,cost;
};

vector<long long> p;
vector<long long> d;

vector<vector<S>> edge;
vector<long long> use;
void init(){
  p.clear();
  d.clear();
  edge.clear();
  use.clear();
}


bool input(){
  long long n;
  cin>>n;
  if(n==0)return false;
  for(long long i=0;i<n-1;i++){
    long long tmp;
    cin>>tmp;
    p.push_back(tmp);
  }
  for(long long i=0;i<n-1;i++){
    long long tmp;
    cin>>tmp;
    d.push_back(tmp);
  }
  return true;
}



void make_edge(){
  edge.clear();
  edge.resize(p.size()+1);
  for(long long i=0;i<p.size();i++){
    long long c = i+1;
    long long to = p[i]-1;
    edge[c].push_back(S{to,d[i]});
    edge[to].push_back(S{c,d[i]});
  }
}


long long hoge(long long start){
  stack<S> st;
  st.push(S{start,0});
  vector<long long> use1(use.size(),-1);
  while(!st.empty()){
    S now = st.top();st.pop();
    if(use1[now.to]>=0)continue;
    use1[now.to] = now.cost;
    for(long long i=0;i<edge[now.to].size();i++){
      if(use[edge[now.to][i].to]){
        st.push(S{edge[now.to][i].to,now.cost + edge[now.to][i].cost});
      }
    }
  }
  long long ans = 0;
  long long cost = 0;
  for(long long i=0;i<use1.size();i++){
    if(cost<use1[i]){
      cost = use1[i];
      ans = i;
    }
  }
  return ans;
}

long long make_cost(long long start,long long end){
  stack<S> st;
  long long l;
  st.push(S{start,0});
  vector<long long> use1(use.size(),-1);
  while(!st.empty()){
    S now = st.top();st.pop();
    if(use1[now.to]>=0)continue;
    use1[now.to] = now.cost;
    for(long long i=0;i<edge[now.to].size();i++){
      if(use[edge[now.to][i].to]){
        st.push(S{edge[now.to][i].to,now.cost + edge[now.to][i].cost});
      }
    }
  }
  return use1[end];
}


long long make_use(){
  use.clear();
  use.resize(edge.size());
  for(long long i=0;i<edge.size();i++){
    use[i] = (edge[i].size()>1);
  }
  long long res = 0;
  for(long long i=0;i<edge.size();i++){
    for(long long j=0;j<edge[i].size();j++){
      if(use[i] && use[edge[i][j].to]){
        res += edge[i][j].cost;
      }
    }
  }
  long long c = 0;
  for(long long i=0;i<use.size();i++){
    for(long long j=0;j<use.size();j++){
      if(use[i]&&use[j]){
        c = max(c,make_cost(i,j));
      }
    }
  }
  res -= c;
  return res;
}


long long solve(){
  long long ans = 0;
  for(long long i=0;i<d.size();i++){
    ans +=d[i];
  }
  long long tmp = make_use();
  ans += tmp;
  return ans;
}


int main(){
  while(init(),input()){
    make_edge();
    long long tmp = solve();
    cout<<tmp<<endl;
    cerr<<tmp<<endl;

  }
}

AOJ 1184: Generic Poker

問題文
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1184

evimaさんのコードを見ながら解いたので完全に理解しているか怪しいが、おそらく日本語の解説が存在しないので書く。
というかもう一回やりたいのでその時用のメモ

間違ってる可能性が高いので間違ってたら教えて下さい

基本的なアイディア

すべてのパターンの手札を生成して、パターンに合致しているならば加算していく。
最後にすべてのパターン数で割ってやれば確率が出る。

手札のパターン生成について

手札はsortedなので、以下の様なコードにすることができる。

vector<int> hands;
void gen(int n){
	if(n==L){
		if(check())ans += count();
		return;
	}
	for(int i=hands[n-1];i<M;i++){
		hands[n]=i;
		gen(n+1);
	}
}

当然だがこれでは時間がかかりすぎるので、いくつか工夫をしてやる必要がある。

数字は高々7種類までしか使われないので、そこをどうにか減らしたい。

問題設定上、ランクの間に間隔がある時はいくつ離れていても問題ないことが読み取れるので、
sortedな手札の場合、隣り合うカードは
①同じ
②一個違い
③間隔がある
の3通りと考えて良い。

そうすると

vector<int> hands;
void gen(int n){
	if(n==L){
		if(check())ans += count();
		return;
	}
	hands[n] = hands[n-1];
	gen(n+1);
	hands[n] = hands[n-1]+1;
	gen(n+1);
	hands[n] = hands[n-1]+2;
	gen(n+1);
}

で十分だということがわかる。

パターンマッチ

上記の生成によって、パターンマッチには十分な情報を持つ手札が生成できている。
例えば手札は

1 1 2 4 4

のようになっている

①a b cの数字が決まっているとすると
それぞれのランクのカードが最低限何枚必要かがわかる(*はあまりを適当に割り振れば良いので無視できる)
最低限必要な枚数がわかっていればそれ以上手札にあるかチェックすれば良い。

②a b cの数字の決定の仕方
手札が決まっているということは、a b c は(もし+が存在すれば)必ず存在するので、
a b cが手札のどの札かというのを適当に仮定してやると、数字が決まる。
この時別に同じ札を選んじゃったりしてても、①の方法でチェックして大丈夫なら、マッチしていることは明らかなので良い。

マッチしたパターンの加算

パターンはもちろん本当はもっと数が多いのを減らしているので、パターンマッチが成功した時はちゃんと計算して足さないといけない。

N=3 M=10
1 1 2 4 4

の時のパターンの数を数える。

まず、それぞれの数について
1→3C2
2→3C1
4→3C2
なので、3C2 * 3C1 * 3C2

あとは生成パターンにおける数がどのランクになるかの計算をする。
間隔が開いているのは2-4の間なので、この手札にあるランクをMの数字の列から取り出すのは
f:id:kyos1704:20140523003420p:plain
上の2つの枠が、矢印のどこに入るかを求めれば良い。
2つの枠は順番は変わってはいけないので、[矢印の数]C[上の枠の数]のようなことをすれば求まる
( 今回は(7+1)C(2) )
あとはそれぞれについて全部かけてやればよい。

TopCoder Open 2014 MM Round1 記録

最終提出コードのサンプル結果
Example scores:
0) 5630.0
1) 7682.0
2) 5552.0
3) 7293.0
4) 4670.0
5) 7557.0
6) 7050.0
7) 5666.0
8) 5677.0
9) 5746.0

最終提出スコア
388718.36

コード
https://bitbucket.org/kyos1704/tcomm1/src/a105196f1087812a155474408d88052603ba4b58/make/a.cpp?at=master

細かいことは週末に書きますぱたり

AOJ 0121 Seven Puzzle

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0121

問題文は上から


パズルを解くまでの最小手数を調べる問題。
パスのコストが1の最短経路問題に帰着できることまではすぐ思いつくと思う。
この時点で解法が幅優先探索っぽいことは即座にわかる。

この問題のポイントは終点がひとつ(状態01234567)であるということ。
終点と始点とすべてのパスの方向を逆転させても同じ問題なので、
終点からすべての状態への遷移にコストがどれだけかかるかを記憶しておくという方法が使える。
MLEが怖いが、状態数が 8!=40320 しかないので、変なことしなきゃ大丈夫。
各クエリに対して、O([状態数])でやると怖いので、mapを使ってO(log[状態数])ぐらいにしてやるといい感じになる。

全体はO([状態数]log[状態数]+[クエリ数]log[状態数])

#include<iostnream>
#include<map>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;

map<vector<int>,int> dp;
//状態を渡すとそこから遷移する状態を作成して返す
vector<vector<int>> next(vector<int> pre){
  vector<vector<int>> res;
  int zero = pre[0];
  if(zero!=1&&zero!=5){
    vector<int> tmp = pre;
    swap(tmp[zero],tmp[zero-1]);
    tmp[0]=zero-1;
    res.push_back(tmp);
  }
  if(zero!=4&&zero!=8){
    vector<int> tmp = pre;
    swap(tmp[zero],tmp[zero+1]);
    tmp[0]=zero+1;
    res.push_back(tmp);
  }
  if(zero>4){
    vector<int> tmp = pre;
    swap(tmp[zero-4],tmp[zero]);
    tmp[0]=zero-4;
    res.push_back(tmp);
  }else{
    vector<int> tmp = pre;
    swap(tmp[zero],tmp[zero+4]);
    tmp[0]=zero+4;
    res.push_back(tmp);
  }
  return res;
}
//テーブル作成 幅優先探索やってメモする
void init(){
  vector<int> s = {1,0,1,2,3,4,5,6,7};
  queue<vector<int>> q;
  dp.clear();
  dp[s] = 0;
  q.push(s);
  while(!q.empty()){
    vector<int> tmp = q.front();q.pop();
    vector<vector<int>> n = next(tmp);
    for(int i=0;i<n.size();i++){
      if(dp.count(n[i])==0){
        dp[n[i]]=dp[tmp]+1;
        q.push(n[i]);
      }
    }
  }
}


int main(){
  init();
  vector<int> in(9);
  //各クエリに対する応答
  while(cin>>in[1]>>in[2]>>in[3]>>in[4]>>in[5]>>in[6]>>in[7]>>in[8] ){
    for(int i=1;i<in.size();i++){
      if(in[i]==0){
        in[0]=i;
      }
    }
    cout<<dp[in]<<endl;
    in.clear();
    in.resize(9);
  }
}