2013年12月31日火曜日

AOJ0214 秋のイルミネーション

解法


四角形の交差判定が出来れば勝ち。
あとはUnionFind使っただけ。

四角形の交差判定は、
1.aがbの中にある
2.bがaの中にある
3.aの各辺がbの各辺でどれか交差する
こんな感じでやった。

ライブラリ作ったのでやってみたけど、ライブラリあるといい(小並感)

ソース


#include<iostream>
#include<complex>
#include<vector>
using namespace std;
typedef complex < double > P;
typedef vector< P > G;
vector< G > g;
const double EPS = 1e-8;
const double INF = 1e12;
double cross(const P& a,const P& b){ //外積
  return imag(conj(a) * b);
}
double dot(const P& a,const P& b){ // 内積
  return real(conj(a) * b);
}
struct L: vector < P >{
  L(const P& a,const P& b){
    push_back(a);
    push_back(b);
  }
};
int ccw(P a,P b,P c){
  b -= a; c -= a;
  if(cross(b,c) > 0) return 1;
  if(cross(b,c) < 0) return -1;
  if(dot(b,c) < 0) return 2; // c--a--b
  if(norm(b) < norm(c)) return -2; // a--b--c
  return 0; // a--c--b
}
bool intersectSS(const L& s,const L& t){ //交差判定 線分:線分
  return ccw(s[0],s[1],t[0]) * ccw(s[0],s[1],t[1]) <= 0 &&
    ccw(t[0],t[1],s[0]) * ccw(t[0],t[1],s[1]) <= 0;
}
#define curr(P,i) P[i]
#define next(P,i) P[(i+1)%P.size()]
bool conteins(const G& Q, const P& p){
  bool in = false;
  for(int i = 0 ; i < Q.size() ; i++ ){
    P a = curr(Q,i) - p, b = next(Q,i) - p;
    if(imag(a) > imag(b)) swap(a,b);
    if(imag(a) <= 0 && 0 < imag(b) && cross(a,b) < 0) in = !in;
    if(cross(a,b) == 0 && dot(a,b) <= 0) return true;
  }
  return in;
}
bool intersect( int x, int y){ //交差判定 g[x] == g[y]
  // g[x] or g[y] が包容
  for(int i = 0 ; i < 4 ; i++ ){
    if(conteins(g[y],g[x][i]) || conteins(g[x],g[y][i])) return true;
  }
  // g[x] と g[y]の辺が交差
  for(int i = 0 ; i < 4 ; i++ ){
    for(int j = 0 ; j < 4 ; j++ ){
      if(intersectSS(L(curr(g[x],i),next(g[x],i)),L(curr(g[y],j),next(g[y],j)))) return true;
    }
  }
  return false;
}
//UnionFind
vector< int > rank, p;
void link(int x,int y){
  if(rank[x] > rank[y]) p[y] = x;
  else{
    p[x] = y;
    if(rank[x] == rank[y]) rank[y]++;
  }
}
void init(int size){
  rank.resize(size);
  p.resize(size);
  for(int i = 0 ; i < size ; i++ ){
    p[i] = i, rank[i] = 0;
  }
}
int findSet(int x){
  if(x != p[x]) p[x] = findSet(p[x]);
  return p[x];
}
void Union(int x,int y){
  link(findSet(x),findSet(y));
}
bool isSame(int x,int y){
  return findSet(x) == findSet(y);
}

int main(){
  int N, M;
  while(cin >> N , N){
    for(int i = 0 ; i < N ; i++ ){
      cin >> M;
      g.resize(M);
      init(M);
      for(int j = 0 ; j < M ; j++ ){
        for(int k = 0 ; k < 4 ; k++ ){
          P buff;
          cin >> buff.real() >> buff.imag();
          g[j].push_back(buff);
        }
      }
      for(int j = 0 ; j < M ; j++ ){
        for(int k = 0 ; k < j ; k++ ){
          if(!isSame( j, k) && intersect( j, k)) Union( j, k);
        }
      }
      int ret = 0;
      for(int j = 0 ; j < M ; j++ ){
        if(findSet(j) == j) ret++;
      }
      cout << ret << endl;
      g.clear();
    }
  }
}

2013年12月26日木曜日

AOJ0213 土地分割

たぶん2013年最後の更新。

解法


DFSにパターン数を足したような再帰すると良い。
枝狩りは、『面積 % 高さ != 0』のときと『すでに誰かの土地がある』とこらへん。
なんかうまく全探できなかったので修行が必要かも。あんまり変数をグローバル化しすぎると良くないね(確信)
計算量が怪しかったが普通に良かった。

ソース


#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
#include<cstring>
using namespace std;
#define fr first
#define sc second
typedef pair < int , int > Pi;
typedef pair < int , Pi > Pii;
  
int h, w, n, ans[10][10];
vector< Pii > memo;
  
int wy, wx;
void plus_n( int y, int x, int nt){
  wy = y;
  wx = ( x + nt ) % w;
  if(!wx) wy++;
  return ;
}
 
int dfs( const int mas[][10], const int used, const int y, const int x){
  if( y == h){ //exit
    memcpy( ans, mas, sizeof(int)*100);
    return 1;
  }
  int ret = 0;
  if( mas[y][x] != 0){
    plus_n( y, x, 1);
    ret += dfs( mas, used, wy, wx);
  }else{
    for(int bit = 0 ; bit < n ; bit++ ){
      if( (used >> bit) & 1 ) continue; // もうやってある
  
      for(int i = 1 ; i <= memo[bit].fr ; i++ ){ //高さ決定
        if( memo[bit].fr % i != 0 ) continue; // 長方形無理
        int height = i, width = memo[bit].fr / i;
  
        if( y <= memo[bit].sc.fr && y > memo[bit].sc.fr - height &&
            x <= memo[bit].sc.sc && x > memo[bit].sc.sc - width &&
            y + height - 1 < h && x + width - 1 < w){
          int java[10][10];
          bool ok = true;
          memcpy( java, mas, sizeof(int)*100);
          for(int j = 0 ; j < height ; j++ ){
            for(int k = 0 ; k < width ; k++ ){
              if(java[j + y][k + x]) ok = false;
              java[j + y][k + x] = bit + 1;
            }
          }
          if(ok){
            plus_n( y, x, width);
            ret += dfs( java, used|(1<<bit), wy, wx);
          }
        }
      }
    }
  }
  return ret;
}
int main(){
  while(cin >> w >> h >> n , w){
    memo.resize( n);
    for(int i = 0, bf ; i < n ; i++ ){
      cin >> bf;
      cin >> memo[bf - 1].fr;
    }
    for(int i = 0, c ; i < h ; i++ ){
      for(int j = 0 ; j < w ; j++ ){
        cin >> c;
        if(c)memo[c - 1].sc = Pi( i, j);
      }
    }
    int mas[10][10] = {{}};
    if(dfs( mas, 0, 0, 0) == 1){
      for(int i = 0 ; i < h ; i++ ){
        for(int j = 0 ; j < w ; j++ ){
          cout << ( j ? " " : "" ) << ans[i][j];
        }
        cout << endl;
      }
    }else cout << "NA" << endl;
  }
}

2013年12月23日月曜日

JOI予選2009

レシート


コンパイルせずに通したら1回TLE食らった。ループが10回になっていた。
サンプルは一応通そう(いましめ)
#include<iostream>

using namespace std;

int main(){
  int sum, in;
  while( cin >> sum, sum){
    for(int i = 0 ; i < 9 ; i++ ){
      cin >> in;
      sum -= in;
    }
    cout << sum << endl;
  }
}


すごろく


落ち着いて実装するだけ。
#include<iostream>
#include<algorithm>
 
using namespace std;
 
int main(){
  int n, m, mas[1001];
  while( cin >> n >> m, n){
    for(int i = 1 ; i <= n ; i++ ){
      cin >> mas[i];
    }
    int ret = -1;
    for(int i = 1, now = 1, MLE ; i <= m ; i++ ){
      cin >> MLE;
      now = min( now + MLE, n);
      if( !~ret && now == n) ret = i;
      now = min( now + mas[now], n);
      if( !~ret && now == n) ret = i;
    }
    cout << ret << endl;
  }
}

パーティー


実装楽そうなわーしゃるふろいどを使った。
ただしこれだと実行速度がアレ。
#include<iostream>
#include<algorithm>

using namespace std;

#define INF ( 1 << 15 )

int main(){
  int n, m, mas[501][501];
  while( cin >> n >> m , n){
    fill_n( *mas, 501 * 501, INF);
    for(int i = 0, a, b ; i < m ; i++ ){
      cin >> a >> b;
      mas[a][b] = mas[b][a] = 1;
    }
    for(int i = 1 ; i <= n ; i++ ){
      for(int j = 1 ; j <= n ; j++ ){
        for(int k = 1 ; k <= n ; k++ ){
          mas[j][k] = min( mas[j][k], mas[j][i] + mas[i][k]);
        }
      }
    }
    int ret = 0;
    for(int i = 2 ; i <= n ; i++ ){
      if( !~(mas[1][i]-2) | !(mas[1][i]-2)) ret++;
    }
    cout << ret << endl;
  }
}

カード並べ


さっきからバグらせすぎなんだが(震え声) 眠いからかな?
bitで使ったカードを保存しながらsetに突っ込む。
#include<iostream>
#include<algorithm>
#include<string>
#include<set>

using namespace std;

#define INF ( 1 << 15 )

string array[10];
int n, k;
static set < string > ret;

void rec( string s, int bit, int cnt){
  if(cnt == k){
    ret.insert( s);
    return;
  }
  for(int i = 0 ; i < n ; i++ ){
    if((bit >> i) & 1) rec( s + array[i], bit&~(1<<i), cnt + 1);
  }
}
int main(){
  while(cin >> n >> k , n){
    for(int i = 0 ; i < n ; i++ ){
      cin >> array[i];
    }
    rec( "", (1 << n) - 1, 0);
    cout << ret.size() << endl;
    ret.clear();
  }
}

通勤経路


頑張って場合分けしてメモ化再帰。
多分以下の5つに場合分け出来る。
① スタート地点( 次どっちも行けて、その次どっちも曲がれる)
② 上上で来た( 次どっちも行けて、右に曲がった場合その次曲がれない)
③ 右右で来た( 次どっちも行けて、上に曲がった場合その次曲がれない)
④ 右上で来た( 次は上しか行けないけど、その次どっちも曲がれる)
⑤ 上右で来た( 次は右しか行けないけど、その次どっちも曲がれる)

#include<iostream>
#include<algorithm>

using namespace std;

const int MOD = 100000;
int w, h, dp[100][100][4];

int rec( int y, int x, int j){
  if( y >= h || x >= w) return 0;
  if( ~dp[y][x][j]) return dp[y][x][j];
  if( y == h - 1 && x == w - 1) return 1;

  int ret;
  if(!y && !x) ret = rec( y + 1, x, 0) + rec( y, x + 1, 1);
  else if( j == 0 ) ret = rec( y + 1, x, 0) + rec( y, x + 1, 3);
  else if( j == 1 ) ret = rec( y + 1, x, 2) + rec( y, x + 1, 1);
  else if( j == 2 ) ret = rec( y + 1, x, 0);
  else if( j == 3 ) ret = rec( y, x + 1, 1);

  return dp[y][x][j] = ret % MOD;
}
int main(){
  while( cin >> w >> h, w){
    fill_n( **dp, 100 * 100 * 4, -1);
    cout << rec( 0, 0, 0) << endl;
  }
}

方向音痴のトナカイ


眠いので前やったソース。もうちょっと早い解法があるのでまた今度やる。
普通にDFSやってるぽい(適当)
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
 
using namespace std;
 
typedef pair<int,int> Pt;
#define fr first
#define sc second
 
int mas[10][10],h,w,dx[]={0,1,-1,0},dy[]={1,0,0,-1};
Pt gl;
Pt Move(Pt p,int n){
  while(true){
    p.fr += dx[n] , p.sc += dy[n];
    if(p.fr >= 0 && p.fr < h && p.sc >= 0 && p.sc < w){
      if( mas[p.fr][p.sc] == 1 ) return p;
    }else return Pt(-1,-1);
  }
}
int solve(Pt p,int home){
  if( !home ){
    return ( p.fr == gl.fr || p.sc == gl.sc ? 1 : 0 );
  }
  int rec = 0;
  for(int i=0;i<4;i++){
    Pt s = Move(p,i);
    if(s.fr == -1) continue;
    mas[s.fr][s.sc] = -1;
    rec += solve(s,home-1);
    mas[s.fr][s.sc] = 1;
  }
  return rec;
}
int main(){
  while(cin >> w >> h && w){
    int home = 0;
    for(int i=0;i<h;i++){
      for(int j=0;j<w;j++){
        cin >> mas[i][j];
        if( mas[i][j] == 1 ) home++;
        else if(mas[i][j] == 2 ) gl = Pt(i,j);
      }
    }
    cout << solve(gl,home) << endl;
  }
}

2013年12月22日日曜日

AOJ0191 Baby Tree

解法


自明なDP。
1回目に与えた肥料だけでは、苗木の大きさが変わりません。
2回目以降は、その回に与えた肥料と、その直前に与えた肥料との組み合わせによって苗木に 影響を与えます。良い影響を与えると苗木が伸び、悪い影響を与えると苗木が縮んでしまうこともあります。
ここからすべてを察した。

ソース


#include<iostream>
#include<iomanip>
#include<algorithm>
using namespace std;
double dp[100][100],g[100][100];
int main(){
  int n, m;
  while(cin >> n >> m , n){
    for(int i = 0 ; i < n ; i++ )
      for(int j = 0 ; j < n ; j++ ) cin >> g[i][j];
    fill_n( dp[0], n, 1.0);
    fill_n( dp[1], 9900, 0.0);
    for(int i = 1 ; i < m ; i++ ) //回
      for(int prev = 0 ; prev < n ; prev++ ) // prev
        for(int now = 0 ; now < n ; now++ ) // now
          dp[i][now] = max( dp[i][now], dp[i - 1][prev] * g[prev][now]);
 
    cout << fixed << setprecision(2) << *max_element( dp[m - 1], dp[m - 1] + n ) << endl;
  }
}

2013年12月18日水曜日

AOJ0210 ザ・スクエアーズ

解法


誤読していた。向きを変えるので1秒、動こうとするので1秒とかやって訳の分からないことをしていた。
向きを変えるステップは自明なので簡単だが、移動するステップが面倒。
最初人視点でやっていたので詰んでしまったが、マス視点でやってあとは頑張った。
dxとかdyとかdmとか4次元配列になってるけど、これはただ求める計算式に至らなかっただけ。
良い感じにシュミレーションすれば勝てる。

ソース


#include<iostream>
#include<vector>
#include<map>
#include<string>
using namespace std;
typedef pair < int , int > Pi;
typedef pair < int , Pi > Pii;
#define fr first
#define sc second
const int dy[][4] = {{1,0,-1,0},{0,-1,0,1},{-1,0,1,0},{0,1,0,-1}};
const int dx[][4] = {{0,1,0,-1},{1,0,-1,0},{0,-1,0,1},{-1,0,1,0}};
const int dm[][4] = {{3,0,1,2},{0,1,2,3},{1,2,3,0},{2,3,0,1}};
const int my[] = { 0, -1, 0, 1}, mx[] = { 1, 0, -1, 0};
const int md[] = { 2, 3, 0, 1};
int w, h;
char mas[30][30];
vector< Pii > vc;
string tmp = "ENWS";
int search( int y , int x){
  for(int i = 0 ; i < vc.size() ; i++ ){
    if(Pi(y,x) == vc[i].sc){
      return i;
    }
  }
  return -1;
}
vector< Pii >  solve(){
  vector< Pii > java;
  for(int i = 0 ; i < vc.size() ; i++ ){
    //change
    for(int j = 0 ; j < 4 ; j++ ){
      int yy = vc[i].sc.fr + dy[vc[i].fr][j];
      int xx = vc[i].sc.sc + dx[vc[i].fr][j];
      if(mas[yy][xx] != '#' && search(yy,xx) == -1){
        vc[i].fr = dm[vc[i].fr][j];
        break;
      }
    }
  }
  //push
  bool list[1000] = {};
  for(int i = 0 ; i < h ; i++ ){
    for(int j = 0 ; j < w ; j++ ){
      if(mas[i][j] == '#' || search(i,j) != -1) continue;
      for(int k = 0 ; k < 4 ; k++ ){
        int ny = i + my[k], nx = j + mx[k], iti;
        if((iti = search(ny,nx)) == -1) continue;
        if(vc[iti].fr == md[k]){
          if(mas[i][j] != 'X') java.push_back(Pii(vc[iti].fr,Pi(i,j)));
          list[iti] = true;
          break;
        }
      }
    }
  }
  for(int i = 0 ; i < vc.size() ; i++ ){
    if(!list[i]) java.push_back(vc[i]);
  }
  return java;
}
int main(){
  while(cin >> w >> h , w){
    for(int i = 0 ; i < h ; i++ ){
      for(int j = 0 ; j < w ; j++ ){
        cin >> mas[i][j];
        if(tmp.find(mas[i][j]) != string::npos){
          vc.push_back(Pii( tmp.find(mas[i][j]), Pi( i, j)));
        }
      }
    }
    int ret = 0;
    while(ret <= 180 && !vc.empty()){
      vc = solve();
      ret++;
    }
    if(ret == 181) cout << "NA" << endl;
    else cout << ret << endl;
    vc.clear();
  }
}

AOJ1155 如何に汝を満足せしめむ? いざ数え上げむ…

解法


最近構文解析にハマるワタクシ。
AOJ1244 Molecular FormulaとかAOJ2401 恒等式とか以前ブログに取り上げたと思う。
三値論理の式が与えられてホゲってしまう。
なんか論理積とか論理和とか否定とか考えれば出来そうだけど、面倒臭かったのでテーブル作った。

ソース


#include<iostream>
#include<string>
#include<cctype>
#include<algorithm>
using namespace std;
typedef string::const_iterator Cursol;
int P, Q, R;
int tmp1[3] = { 2, 1, 0}, tmp2[3][3] = {{0,0,0},{0,1,1},{0,1,2}};
int tmp3[3][3] = {{0,1,2},{1,1,2},{2,2,2}};
 
int formula(Cursol &c){
  int ret ;
  if(*c == 'P') ret = P;
  else if(*c == 'Q') ret = Q;
  else if(*c == 'R') ret = R;
  else if(isdigit(*c)) ret = *c - '0';
  else if(*c == '-') return tmp1[formula(++c)];
  else if(*c == '('){
    ret = formula(++c);
    if(*c == '*') ret = tmp2[ret][formula(++c)];
    else if(*c == '+') ret = tmp3[ret][formula(++c)];
  }
  c++;
  return ret;
}
int main(){
 
  string s;
  Cursol c;
 
  while( cin >> s, s != "."){
    int ret = 0;
    for(P = 0 ; P < 3 ; P++ ){
      for(Q = 0 ; Q < 3 ; Q++ ){
        for(R = 0 ; R < 3 ; R++ ){
          c = s.begin();
          if( 2 == formula(c)) ret++;
        }
      }
    }
    cout << ret << endl;
  }
}

AOJ0244 Bicycle Diet

解法


H,D,C,Lを適当に数字に置き換えて一つにまとめてしまう。
あとは既に行ったケーキ屋さんの情報をbitで持たせて幅優先するだけ。
微妙にバグったけど察して修正した。
最初ケーキ屋さんの数が100に見えて怖かった。問題文はちゃんと読もう(戒め)

ソース


#include<iostream>
#include<queue>
#include<vector>
#include<string>
#include<cstdlib>
using namespace std;
#define fr first
#define sc second
#define INF ( 1 << 30 )
int m, n, k, d;
int min_cost[110][1 << 6];
vector< int > cal;
typedef pair < int , int > Pi;
typedef pair < int , Pi > Pii;
struct edge{
  int to, cost;
  edge(){}
  edge( int to, int cost): to(to), cost(cost){};
};
vector < edge > info[110];
int java(string& s){
  if(s[0] == 'H') return 0; //MyHome
  if(s[0] == 'D') return 1; //市役所
  if(s[0] == 'C') return atoi(s.substr(1).c_str()) + 1; //ケーキ屋さん
  if(s[0] == 'L') return atoi(s.substr(1).c_str()) + m + 1; //ランドマーク
}
int bfs(){
  int ret = INF;
  fill_n(*min_cost,110*(1<<6),INF);
  queue< Pii > que;
  que.push(Pii( 0, Pi( 0, (1<<m) - 1)));
  min_cost[0][(1<<m)-1] = true;
  while(!que.empty()){
    Pii p = que.front();
    que.pop();
    if(p.sc.fr == 1) ret = min( ret, p.fr);
    if(min_cost[p.sc.fr][p.sc.sc] < p.fr ) continue;
    for(int i = 0 ; i < info[p.sc.fr].size() ; i++ ){
      edge e = info[p.sc.fr][i];
      int cost = p.fr + e.cost * k, bit = p.sc.sc;
      if( e.to >= 2 && e.to < 2 + m ){
        if((bit >> (e.to-2)) & 1){
          cost -= cal[e.to-2];
          bit &= ~(1 << (e.to-2));
        }else continue;
      }
      if( cost < min_cost[e.to][bit] ){
        que.push(Pii( cost,Pi( e.to, bit)));
        min_cost[e.to][bit] = cost;
      }
    }
  }
  return ret;
}
int main(){
  while(cin >> m >> n >> k >> d , m){
    cal.resize(m);
    for(int i = 0 ; i < m ; i++ ){
      cin >> cal[i];
    }
    for(int i = 0 ; i < d ; i++ ){
      string s1, s2;
      int cost;
      cin >> s1 >> s2 >> cost;
      info[java(s1)].push_back(edge( java(s2), cost));
      info[java(s2)].push_back(edge( java(s1), cost));
    }
    cout << bfs() << endl;
    for(int i = 0 ; i < 110 ; i++ ) info[i].clear();
  }
}

2013年12月16日月曜日

JOI予選2014 part2

5問目

普通にDijkstraやったら通ってしまったのでなぜこの問題をやらなかったのか後悔せざる得ない。
20分位で書けてしまってテストケースすべて1発で通してしまったので萎えた。
#include<iostream>
#include<queue>
#include<vector>
#include<map>
using namespace std;
#define fr first
#define sc second
typedef pair < int , int > Pi;
struct edge{
  int limit, cost;
  vector< int > node , cango;
} info[5001];
int n, k;
bool used[5001];

int Dijkstra(){
  priority_queue< Pi , vector< Pi > , greater< Pi > > que;
  for( que.push(Pi(0,0)) ; !que.empty() ; que.pop()){
    Pi p = que.top();
    if(used[p.sc]++) continue;
    if(p.sc == n - 1) return p.fr;
    for(int i = 0 ; i < info[p.sc].cango.size() ; i++ ){
      que.push(Pi(p.fr + info[p.sc].cost,info[p.sc].cango[i]));
    }
  }
}
int main(){
  cin >> n >> k;
  for(int i = 0 ; i < n ; i++ ){
    cin >> info[i].cost >> info[i].limit;
  }
  for(int i = 0, a, b ; i < k ; i++ ){
    cin >> a >> b;
    info[--a].node.push_back(--b);
    info[b].node.push_back(a);
  }

  queue< Pi > que;
  for(int i = 0 ; fill_n(used, 5001, false), i < n ; i++ ){
    for( que.push(Pi(i,info[i].limit)) ; !que.empty() ; que.pop() ){
      Pi p = que.front();
      if(!p.sc) continue;
      for(int j = 0 ; j < info[p.fr].node.size() ; j++ ){
        if(!used[info[p.fr].node[j]]++){
          info[i].cango.push_back(info[p.fr].node[j]);
          que.push(Pi(info[p.fr].node[j],p.sc-1));
        }
      }
    }
  }
  cout << Dijkstra() << endl;
}

2013年12月15日日曜日

JOI予選2014

感想

ああ...(察して
あえて言うなら逝ってしまった

問4

#include<iostream>
#include<string>
using namespace std;
#define MOD 10007
int n, dp[1000][1 << 3];
string s, tmp = "JOI";
int solve(int today, int bit){
  if( today == n ) return 1;
  if( dp[today][bit] ) return dp[today][bit];
  int ret = 0;
  for(int i = 0 ; i < (1 << 3) ; i++ ){
    if(( i >> tmp.find(s[today])) & 1 && i&bit){
      ret += solve( today + 1, i);
    }
  }
  return dp[today][bit] = ret % MOD;
}
int main(){
  cin >> n >> s;
  cout << solve( 0, 1) << endl;
}
自明なのによくわからないことをしてしまった(小並感)
if( today == n + 1 ) と固定されていた、また鍵を気にし過ぎたのがすべての敗因だったかもしれない。
あとほとんど一緒じゃね?って思ってみたり思ってみなかったり。
なんだか困惑と絶望だけが残ってしまった。
1年のうちに本戦行きたかったので留年したい(願望)

AOJ2401 恒等式

問題概要


与えられた等式が,すべての場合において成り立つかどうか判定する。
定数: T, F
変数: a, b, c, d, e, f, g, h, i, j, k
論理否定: -X
論理積: (X*Y)
論理和: (X+Y)
論理包含: (X->Y)

解法


構文解析する。変数が最大11個なのですべての場合において試した。

ソース


#include<iostream>
#include<string>
#include<map>
#include<sstream>
#include<cctype>
#include<algorithm>
using namespace std;
typedef string::const_iterator Cursol;
bool ok;
int bit;
bool Formula(Cursol &c){
  bool ret;
  if(*c == 'T') ret = true;
  else if(*c == 'F') ret = false;
  else if(islower(*c)) ret = bit >> ((*c) - 'a') & 1;
  else if(*c == '-') return !Formula(++c);
  else if(*c == '('){
    ret = Formula(++c);
    if(*c == '+'){
      ret |= Formula(++c);
    }else if(*c == '*'){
      ret &= Formula(++c);
    }else if(*c == '-'){ //->
      c++;
      ret = !ret | Formula(++c);
    }
  }
  c++;
  return ret;
}
int main(){
  string s;
  Cursol c;
  while(cin >> s , ok = true, s != "#"){
    string l = s.substr(0,s.find('='));
    string r = s.substr(s.find('=')+1);

    for(bit = 0 ; bit < (1 << 11) ; bit++ ){
      c = l.begin();
      bool java = Formula(c);
      c = r.begin();
      if( java != Formula(c) ){
 ok = false;
 break;
      }
    }
    if(ok) cout << "YES" << endl;
    else cout << "NO" << endl;
  }
}

AOJ1244 Molecular Formula

問題概要


元素記号と重さの対応表が最初に与えられる。
その後、化学式が与えられるので、その重さを答える。
未知の元素が含まれている場合は"UNKNOWN"と出力する。

<molecule>::=<atom>|<atom><number>|'('<molecule>')'<number>|<molecule><molecule>
<atom>::=<capitaletter>|<capitaletter><smallletter>
<number>::=2|3|4|・・・|97|98|99
<capitalletter>::=A|B|C|・・・|X|Y|Z
<smallletter>::=a|b|c|・・・|x|y|z

解法


BNFに従って構文解析するだけ。

ソース


#include<iostream>
#include<string>
#include<map>
#include<sstream>
#include<cctype>
using namespace std;
typedef string::const_iterator Cursol;
map < string , int > d;
bool ok;
int stoi(string s) {
  int v;
  stringstream ss(s);
  ss>>v;
  return v;
}
int Num(Cursol &c){
  int ret;
  string s;
  s += *c++;
  if(isdigit(*c)) s += *c++;
  return stoi(s);
}
int Atom(Cursol &c){
  int ret;
  string s;
  s += *c++;
  if(islower(*c)) s += *c++;
  ret = d[s];
  if(ret == 0) ok = false;
  return ret;
}
int Molecule(Cursol &c){
  if( *c == '\0' || *c == ')') return 0; 
  int ret = 0;
  if(*c == '('){
    ret = Molecule(++c) * Num(++c);
  }else if(isupper(*c)){
    ret = Atom(c);
    if(isdigit(*c)) ret *= Num(c);
  }
  return ret + Molecule(c);
}
int main(){
  string s;
  while(cin >> s , s != "END_OF_FIRST_PART"){
    cin >> d[s];
  }
  while(cin >> s , s != "0"){
    Cursol c = s.begin();
    ok = true;
    int ret = Molecule(c);
    if(ok) cout << ret << endl;
    else cout << "UNKNOWN" << endl;
  }
}

2013年12月14日土曜日

AOJ0570 ジグザグ数

解法

桁DPと呼ばれるらしい。
a以上b以下のジグザグ数の個数は、b以下のジグザグ数 - a-1以下のジグザグ数で求められる。
dp[自由に選べるか][直前が... 0:沿ってる 1:増 2:減][前の桁の数][余り][今の場所] という配列を用意。
自由に選べるかは、桁の数字に沿わなくなるまでfalseでやる。
余りは、(前の桁までの余り * 10 + 今の桁) % 倍数でやる。

ソース

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
#define MOD 10000
 
string s;
short dp[2][3][10][500][501]; //[free][増減][prev][余り][index]
int m;
 
int solve( bool fr, int updw, int pre, int mod, int index){
  if( index == s.size() ) return !mod;
  if( dp[fr][updw][pre][mod][index] != -1 ) return dp[fr][updw][pre][mod][index];
  int ret = 0, end = fr ? 9 : s[index];
  for(int i = 0, u ; i <= end ; i++ ){
    switch(updw){
    case 0: //さいしょ
      if( pre && pre == i ) continue;
      else if( pre == 0 ) u = 0;
      else if( pre > i ) u = 1;
      else u = 2;
      break;
    case 1: //次あっぷ
      if( pre >= i ) continue;
      else u = 2;
      break;
    case 2: //次だうん
      if( pre <= i ) continue;
      else u = 1;
      break;
    }
    ret += solve( fr|(i!=s[index]), u, i, (mod*10+i)%m, index+1);
  }
  return dp[fr][updw][pre][mod][index] = ret % MOD;
}
 
void java(){
  for(int i = s.size() - 1 ; i >= 0 ; i-- ){
    if(s[i] == 0) s[i] = 9;
    else{
      s[i]--;
      break;
    }
  }
}
 
int main(){
  string s1;
  cin >> s >> s1 >> m;
  for(int i = 0 ; i < s.size() ; i++ ) s[i] -= '0';
  java();
  fill_n( ****dp, 2 * 3 * 10 * 500 * 501, -1);
  int an = solve( 0, 0, 0, 0, 0);
  s = s1;
  for(int i = 0 ; i < s.size() ; i++ ) s[i] -= '0';
  fill_n( ****dp, 2 * 3 * 10 * 500 * 501, -1);
  int bn = solve( 0, 0, 0, 0, 0);
  cout << ( bn - an + MOD ) % MOD << endl;
}

2013年12月9日月曜日

AOJ0215 パチモンクリーチャー

解法

動的計画法じゃないかな?グラフにしたら勝った。
バグ死してた。

ソース

#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<cmath>
using namespace std;
#define INF ( 1 << 30 )
#define fr first
#define sc second
typedef pair < int , int > Pi;
typedef pair < Pi , Pi > Pii;
const int dy[] = { 0, 1, -1, 0}, dx[] = { 1, 0, 0, -1};
int h, w, Sx, Sy, Gx, Gy;
vector< Pi > hoge[5];
int dp[5][1000], num;
char c;
int dis(Pi x1,Pi x2){
  return abs(x1.fr - x2.fr) + abs(x1.sc - x2.sc);
}
int rec(int now,int java){
  if(dp[now][java]) return dp[now][java];
  int ret = INF, next = ( now + 1 ) % 5;
  if(now == num) ret = dis(hoge[now][java],Pi(Gy,Gx));
  else for(int i = 0 ; i < hoge[next].size() ; i++ ){
    ret = min( ret, rec( next, i) + dis(hoge[now][java],hoge[next][i]));
  }
  return dp[now][java] = ret;
}
int main(){
  while(cin >> w >> h , w){
    for(int i = 0 ; i < 5 ; i++ ) hoge[i].clear();
    for(int i = 0 ; i < h ; i++ ){
      for(int j = 0 ; j < w ; j++ ){
        cin >> c;
        if(c == 'S') Sy = i, Sx = j;
        else if(c == 'G') Gy = i, Gx = j;
        else if(c != '.') hoge[c-'1'].push_back(Pi(i,j));
      }
    }
    Pi ret = Pi(INF,INF);
    for(int i = 0 ; i < 5 ; i++ ){
        for(int j = 0 ; j < 5 ; j++ )
          for(int k = 0 ; k < 1000 ; k++ ) dp[j][k] = 0;
      num = ( i + 4 ) % 5;
      int next = ( i + 1 ) % 5, tmp = INF;
      for(int j = 0 ; j < hoge[next].size() ; j++ ){
        ret = min( ret, Pi(rec( next, j) + dis(Pi(Sy,Sx),hoge[next][j]), i+1));
      }
    }
    if( ret.fr == INF ) cout << "NA" << endl;
    else cout << ret.sc << " " << ret.fr << endl;
  }
}

2013年12月7日土曜日

string

暇だったのでまとめてみた。

AOJ2254 最短ルート

解法


bitでいろいろやるのは自明っぽかったので察して、
Dijkstraじゃね?って思ったがMLEで詰んでいた。
動的計画法らしかったのでそうした。
bit[クリアしてるかの状態] = 最短時間

ソース


#include<iostream>
#include<algorithm>
using namespace std;
#define INF ( 1 << 30 )
int N,mas[16][17],dp[1<<17];
 
int rec(int bit){
  if(dp[bit]) return dp[bit];
  if(!bit) return 0;
 
  int ret = INF;
  for(int i = 0 ; i < N ; i++ ){
    if(!(bit & (1 << i))) continue;
    int min_cost = mas[i][0];
    for(int j = 0 ; j < N ; j++ ){
      if(i != j && !(bit & (1 << j))) min_cost = min( min_cost, mas[i][j+1]);
    }
    ret = min( ret, min_cost + rec(bit&~(1 << i)));
  }
  return dp[bit] = ret;
}
 
int main(){
  while(cin >> N , N ){
    fill_n( dp, 1<<17, 0);
    for(int i = 0 ; i < N ; i++ ){
      for(int j = 0 ; j <= N ; j++ ){
        cin >> mas[i][j];
      }
    }
    cout << rec((1 << N)-1) << endl;
  }
}
ループ
#include<cstdio>
#include<algorithm>
using namespace std;
#define INF ( 1 << 25 )
int N,mas[16][17],dp[1<<16];
int solve(){
  for(int i = 0 ; i < 1 << N ; i++ ) dp[i] = INF;
  dp[(1 << N) - 1] = 0;
  for(int bit = (1 << N) - 1 ; bit >= 0 ; bit-- ){
    for(int i = 0 ; i < N ; i++ ){
      if(!(bit & (1 << i))) continue;
      int min_cost = mas[i][0];
      for(int j = 0 ; j < N ; j++ ){
        if(!(bit & (1 << j))) min_cost = min( min_cost, mas[i][j+1]);
      }
      dp[bit&~(1<<i)] = min(dp[bit&~(1<<i)],min_cost + dp[bit]);
    }
  }
  return dp[0];
}
int main(){
  while(scanf("%d",&N), N ){
    for(int i = 0 ; i < N ; i++ ){
      for(int j = 0 ; j <= N ; j++ ){
        scanf("%d",&mas[i][j]);
      }
    }
    printf("%d\n",solve());
  }
}


最初浮かんだ解法,Dijjkstraは状態数が多すぎた。
used[16][1<<16] = used[今の場所][bit]。今の場所が不要なことを察したかった。
上のDPと似てるけど修正したやつ。
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
#define fr first
#define sc second
#define INF ( 1 << 30 )
typedef pair < int , int > P;
int N, cost[16][17];
bool used[1<<16];
int Dijkstra(){
  priority_queue< P , vector<P> , greater<P> > que;
  for(int i = 0 ; i < N ; i++ ){
    que.push(P(cost[i][0],((1<<N)-1)&~(1<<i)));
  }
  fill_n( used, 1<<16, false);
  while(!que.empty()){
    P p = que.top();
    que.pop();
    if(p.sc == 0) return p.fr;
    if(used[p.sc]++) continue;
    for(int i = 0 ; i < N ; i++ ){
      if(!((p.sc >> i) & 1)) continue;
      int ret = cost[i][0];
      for(int j = 0 ; j < N ; j++ ){
        if(i != j && !((p.sc >> j) & 1)) ret = min(ret, cost[i][j+1]);
      }
      que.push(P(p.fr+ret,p.sc&~(1 << i)));
    }
  }
  return -1;
}
int main(){
  while(cin >> N , N){
    for(int i = 0 ; i < N ; i++ ){
      for(int j = 0 ; j <= N ; j++ ){
        cin >> cost[i][j];
      }
    }
    cout << Dijkstra() << endl;
  }
}

2013年12月1日日曜日

JOI2008 予選

タイムカード


秒に直して割ったり余りを求めたりして頑張る。
#include<iostream>
using namespace std;
int main(){
  int h1, m1, s1, h2, m2, s2;
  while(cin >> h1 >> m1 >> s1 >> h2 >> m2 >> s2){
    int tim = h2 * 3600 + m2 * 60 + s2 - h1 * 3600 - m1 * 60 - s1;
    cout << tim / 3600 << " " << tim % 3600 / 60 << " " << tim % 60 << endl;
  }
}

コンテスト


普通にやった。
#include<iostream>
#include<vector>
#include<algorithm>
#include<numeric>
using namespace std;
int main(){
  vector< int > A(10),B(10);
  for(int i = 0 ; i < 10 ; i++ ) cin >> A[i];
  for(int i = 0 ; i < 10 ; i++ ) cin >> B[i];
  sort(A.rbegin(),A.rend());
  sort(B.rbegin(),B.rend());
  cout << accumulate(A.begin(),A.begin()+3,0) << " "
       << accumulate(B.begin(),B.begin()+3,0) << endl;
}

連鎖


ちょっと頭使った。とりあえず全通り試す方針で、上へ下へと広げて連鎖をチェックする方針でいった。
#include<iostream>
#include<algorithm>
using namespace std;
#define INF ( 1 << 30 )
int n, d, ret, chain[10000];
int solve(int left){
  int right = left + 1, all = n;
  while(true){
    int color = chain[left], dis = 0;
    while( chain[left] == color && left >= 0 && ++dis ) left--;
    while( chain[right] == color && right < n && ++dis ) right++;
    if(dis < 4) return all;
    all -= dis;
  }
}
int main(){
  while(cin >> n, n){
    int ret = INF;
    for(int i = 0 ; i < n ; i++ ) cin >> chain[i];
    for(int i = 0 ; i < n ; i++ ){
      for(int j = 1 ; j < 4 ; j++ ){
        swap( chain[i], j);
        ret = min( ret, solve( i));
        swap( chain[i], j);
      }
    }
    cout << ret << endl;
  }
}

薄氷渡り


DFSでやったらすぐできた。
#include<iostream>
#include<algorithm>
using namespace std;
const int dy[] = { 1, 0, 0, -1}, dx[] = { 0, -1, 1, 0};
int h, w, mas[90][90];
bool used[90][90];
int dfs( int y, int x){
  if(used[y][x] || !mas[y][x]) return 0;
  used[y][x] = true;
  int ret = 0;
  for(int i = 0 ; i < 4 ; i++ ){
    int ny = y + dy[i], nx = x + dx[i];
    if(ny >= 0 && ny < h && nx >= 0 && nx < w){
      ret = max( ret, dfs( ny, nx) + 1);
    }
  }
  used[y][x] = false;
  return ret;
}
int main(){
  while(cin >> h >> w, h){
    for(int i = 0 ; i < h ; i++ ){
      for(int j = 0 ; j < w ; j++ ){
        cin >> mas[i][j];
      }
    }
    int ret = 0;
    for(int i = 0 ; i < h ; i++ ){
      for(int j = 0 ; j < w ; j++ ){
        ret = max( ret, dfs( i, j));
      }
    }
    cout << ret << endl;
  }
}

シャッフル


カードの枚数が10^9なので普通にやったら間に合わないのは自明。
シャッフルの回数が5000回以下なので,[始まりの番号,終わりの番号]を
pairで持たせて凄く頑張って実装すればできる気がする(震え声)。
どう考えてもバグって詰むのも自明だけど仕方ない。
#include<iostream>
#include<queue>
#include<map>
using namespace std;
#define fr first
#define sc second
typedef pair< int , int > P;
int n, m;
deque< P > d;
int _sum(P p){
  return p.sc - p.fr + 1;
}
int query(int& p,int& q,int& r){
  int ret = 0, ans = 0;
  while(ret + _sum(d.front()) < p){
    ret += _sum(d.front());
    d.pop_front();
  }
  if(ret != p - 1) d.front().fr += p - ret;
  ret = p;
  while(ret + _sum(d.front()) <= q){
    if(d.front().sc <= r) ans += _sum(d.front());
    else if(d.front().fr <= r) ans += _sum(P(d.front().fr,r));
    ret += _sum(d.front());
    d.pop_front();
  }
  if(!d.empty() && ret != q){
    P p = P(d.front().fr,d.front().fr + q - ret - 1);
    if(p.sc <= r) ans += _sum(P(p.fr,p.sc));
    else if(p.fr <= r) ans += _sum(P(p.fr,r));
  }
  return ans;
}
void shuffle(int* xy){
  deque< P > tmp[2];
  int ret = 0;
  for(int i = 0 ; i < 2 ; i++ ){
    while( ret + _sum(d.front()) <= xy[i]){
      tmp[i].push_back(d.front());
      ret += _sum(d.front());
      d.pop_front();
    }
    if(ret != xy[i]){
      tmp[i].push_back(P(d.front().fr,d.front().fr + xy[i] - ret - 1));
      d.front().fr = d.front().fr + xy[i] - ret;
      ret = xy[i];
    }
  }
  d.insert(d.end(),tmp[1].begin(),tmp[1].end());
  d.insert(d.end(),tmp[0].begin(),tmp[0].end()); 
}
int main(){
  while(cin >> n , n){
    cin >> m;
    d.push_back( P( 1, n));
    int p, q, r;
    cin >> p >> q >> r;
    while(m--){
      int xy[2];
      cin >> xy[0] >> xy[1];
      shuffle(xy);
    }
    cout << query(--p, q, r) << endl;
    d.clear();
  }
}

ビンゴ


上から下,左から右に向かって数字が大きくなっていることを利用する。
dp[今いる場所][今いる場所までの合計] = パターンの数
#include<iostream>
using namespace std;
int main(){
  int N, M, S, dp[51][3001];
  while(cin >> N >> M >> S , N){
    fill_n(*dp,51*3001,0);
    dp[0][0] = 1;
    for(int i = 1 ; i <= M ; i++ ){
      for(int j = N * N ; j ; j-- ){
        for(int k = i ; k <= S ; k++ ){
          dp[j][k] = (dp[j][k] + dp[j-1][k-i]) % 100000;
        }
      }
    }
    cout << dp[N*N][S] << endl;
  }
}

2013年11月29日金曜日

AOJ1189 素数洞穴

解法


DFS。途中で1000000とかセグフォで逝っちゃうんじゃね?って思いながら組んでいったが、
案の定逝ってしまったのでBFSに書き直して勝ったと思っていたら負けていた。
素数配列の大きさが100001で一桁足りないじゃんって気付くまで時間がかかった。
洞穴のマップを最初に最大値まで求めて作っておく。あと素数も。
これはdy[],dx[]のアレを考慮してやって、進行方向の左側に数字が入ってなかったら曲がろうという方針。
某副部長兼キチガイが提案してくれたので頭いいなと思った。この実装はすぐできた。
ちゃんと答えまで出たらmax_costをint型からpairにして最後に通った素数の判定を感で付け加えた。

ソース


#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
#define fr first
#define sc second
const int dy[] = { 0, -1, 0, 1}, dx[] = { 1, 0, -1, 0}; //右上左下
typedef pair< int , int > Pt;
int m,n,mas[1005][1005],hw = 1000;
Pt buff[1000001],max_cost[1005][1005];
bool prime[1000001];
void Prime_set(){
  prime[1] = true;
  for(int i = 2 ; i * i < 1000001 ; i++ ){
    if(!prime[i]) for(int j = i + i ; j < 1000001 ; j += i ) prime[j] = true;
  }
}
bool isover(int y,int x){
  return x < 0 || !mas[y][x] || mas[y][x] > m;
}
void make_map(){
  fill_n(*mas,1005*1005,0);
  int cnt = 1,muki = 3;
  Pt now = Pt( hw / 2,  hw / 2),St;
  while(cnt <= 1000000){
    buff[cnt] = Pt(now.fr,now.sc);
    mas[now.fr][now.sc] = cnt++;
    Pt magaru = Pt( now.fr + dy[(muki + 1) % 4], now.sc + dx[(muki + 1) % 4]);
    if(!mas[magaru.fr][magaru.sc]) muki = (muki + 1) % 4, now = magaru;
    else now.fr += dy[muki], now.sc += dx[muki];
  }
}
Pt dfs(Pt now){
  if(max_cost[now.fr][now.sc] != Pt(-1,-1)) return max_cost[now.fr][now.sc];
  Pt ret = Pt();
  for(int i = 0 ; i < 3 ; i++ ){
    int nx = now.sc + dx[i];
    if(!isover(now.fr+1,nx)) ret = max(ret,dfs(Pt(now.fr+1,nx)));
  }
  if(!ret.sc && !prime[mas[now.fr][now.sc]]) ret.sc = mas[now.fr][now.sc];
  return max_cost[now.fr][now.sc] = Pt(ret.fr+!prime[mas[now.fr][now.sc]],ret.sc);
}
int main(){
  Prime_set();
  make_map();
  while(cin >> m >> n , m){
    fill_n(*max_cost,1005*1005,Pt(-1,-1));
    Pt ans = dfs(buff[n]);
    cout << ans.fr << " " << ans.sc << endl;
  }
}

2013年11月28日木曜日

AOJ2399 Save Your Privacy!

解法


漏洩した人物の情報をすべて知っていた人は怪しい。
std::includes(itr first1,itr last1,itr first2,itr last2)を使うと単純に済む。
[first1,last1]の中に[first2,last2]が全部含まれていたらtrueが返ってくる。

ソース


#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define ALL(a) a.begin(),a.end()
int main(){
  int n;
  while( cin >> n , n ){
    vector < int > p[n],L;
    for(int i = 0, q, d ; i < n && cin >> q ; sort(ALL(p[i])), i++ ){
      while(q--) cin >> d, p[i].push_back(--d);
    }
    int K, l, ans = -1, flg = -1;
    cin >> K;
    while(K--) cin >> l, L.push_back(--l);
    sort(ALL(L));
    for(int i = 0 ; i < n && flg<=0 ; i++ ){
      if(includes(ALL(p[i]),ALL(L))) ans = i + 1, flg++;
    }
    cout << (flg ? -1 : ans) << endl;
  }
}

2013年11月24日日曜日

AOJ1038 Dr. Nakamura's Lab.

解法


私の苗字じゃないですか!と思って前々からやりたかった問題。
最初priorityつけてたが,遅くなるだけじゃんって気づいて普通のqueでやったら通った。
枝刈りがうまくいかなくて大変だった。最終的には,強引にlonglong型に直して記憶した。
ソース長い。

ソース


#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
#define fr first
#define sc second
#define rep(i,n) for(int i = 0 ; i < n ; i++ )
typedef pair< int , int > Pt;
typedef long long ll;
const int dx[] = { 0, 1, -1, 0}, dy[] = { 1, 0, 0, -1};
struct P{
  Pt nkmr;
  vector< Pt > pnl,cnt;
};
typedef pair< int , P > IP;
Pt Goal;
int h, w;
char mas[10][10];
int search_cnt(int y,int x,P& n){
  rep(i,n.cnt.size()) if(n.cnt[i] == Pt(y,x)) return i;
  return -1;
}
int search_panel(int y,int x,P& n){
  rep(i,n.pnl.size()) if(n.pnl[i] == Pt(y,x)) return i;
  return -1;
}
bool isused(map<ll,bool>& used,P& p){
  ll ret = (p.nkmr.fr << 4) + p.nkmr.sc;
  rep(i,p.pnl.size()){
    ret <<= 4;
    ret += p.pnl[i].fr;
    ret <<= 4;
    ret += p.pnl[i].sc;
  }
  rep(i,p.cnt.size()){
    ret <<= 4;
    ret += p.cnt[i].fr;
    ret <<= 4;
    ret += p.cnt[i].sc;
  }
  if(used[ret]++) return true;
  return false;
}
int bfs(P& St){
  queue<pair< int , P > > que;
  map < ll , bool >  used;
  que.push(IP(0,St));
  while(!que.empty()){
    int _cost = que.front().fr;
    P p = que.front().sc;
    que.pop();
    if(p.nkmr == Goal) return _cost;
    if(isused(used,p)) continue;
    rep(i,4){
      P next = p;
      int ny = p.nkmr.fr + dy[i], nx = p.nkmr.sc + dx[i],contena,panel;
      if(mas[ny][nx] == '#' || search_panel(ny,nx,next) != -1) continue;
      if((contena = search_cnt(ny,nx,next)) != -1){ //コンテナあった
        int ty = ny + dy[i], tx = nx + dx[i];
        if(mas[ty][tx] != '.' || search_cnt(ty,tx,next) != -1) continue;
        while(mas[ty][tx] == '.' &&
              search_cnt(ty,tx,next) == -1 && search_panel(ty,tx,next) == -1){
          ty += dy[i], tx += dx[i];
        }
        if((panel = search_panel(ty,tx,next)) != -1){
          next.cnt[contena] = Pt(-1,-1), next.pnl[panel] = Pt(-1,-1);
        }else next.cnt[contena] = Pt(ty - dy[i],tx - dx[i]);
        que.push(IP(_cost,next));
      }else que.push(IP(_cost+1,(P){Pt(ny,nx),p.pnl,p.cnt}));
    }
  }
  return -1;
}
int main(){
  while(cin >> h >> w , h ){
    P p = (P){Pt(),vector<Pt>(),vector<Pt>()};
    rep(i,h) rep(j,w){
      cin >> mas[i][j];
      if(mas[i][j] == '@') p.nkmr = Pt(i,j), mas[i][j] = '.';
      else if(mas[i][j] == 'w'){
        p.pnl.push_back(Pt(i,j)), mas[i][j] = '.';
      }else if(mas[i][j] == 'c'){
        p.cnt.push_back(Pt(i,j)), mas[i][j] = '.';
      }else if(mas[i][j] == 'E') Goal = Pt(i,j), mas[i][j] = '.';
    }
    cout << bfs(p) << endl;
  }
}

2013年11月23日土曜日

AOJ0202 上司のおごり

解法


良い感じにやる。dp[金額] = 買えるかどうか
dp[金額]がtrueで,素数である最大値を求めれば良い。

ソース


#include<cstdio>
bool dp[1000001];
bool isprime(int& x){
  for(int i = 2 ; i * i <= x ; i++ ) if( x % i == 0) return true;
  return x == 1;
}
int solve(int& i){
  while(true){
    if(dp[i] && !isprime(i)) return i;
    i--;
  }
}
int main(){
  int n,x,data,ans;
  dp[0] = true;
  while(scanf("%d %d",&n,&x) , n){
    for(int i = 1 ; i <= x ; i++ ) dp[i] = false;
    for(int i = 0 ; i < n ; i++ ){
      scanf("%d",&data);
      for(int i = data ; i <= x ; i++ ) if(dp[i-data]) dp[i] = true;
    }
    if(ans = solve(x)) printf("%d\n",ans);
    else printf("NA\n");
  }
}

2013年11月21日木曜日

AOJ0550 お菓子の分割

解法

DPる。但し普通に配列をとると逝ったので、やめよう(戒め)偶奇で分ける
dp[今の位置][A][Aの個数+1] = min(dp[前の位置][A][Aの個数],dp[前の位置][B][Aの個数] + data[今]);
dp[今の位置][B][Aの個数] = min(dp[前の位置][B][Aの個数],dp[前の位置][A][Aの個数] + data[今]);
で幸せになれた(切実)

ソース

#include<iostream>
#include<algorithm>
using namespace std;
#define INF ( 1 << 30 )
int dp[2][2][5001],data[9999];
int main(){
  int n;
  cin >> n;
  fill_n(**dp,2*2*5001,INF);
  dp[0][0][0] = 0;
  for(int i = 1 ; i < n ; i++ ){
    cin >> data[i];
  }
  for(int i = 0 ; i < n ; i++ ){
    for(int j = 0 ; j <= min(n/2,i) ; j++ ){
      dp[(i+1)&1][0][j+1] = min(dp[i&1][0][j],dp[i&1][1][j]+data[i]);
      dp[(i+1)&1][1][j] = min(dp[i&1][1][j],dp[i&1][0][j]+data[i]);
    }
    dp[(i+1)&1][0][0] = INF;
  }
  cout << min(dp[(n/2)%2][0][n/2],dp[(n/2)%2][1][n/2]) << endl;
}

2013年11月18日月曜日

AOJ0245 タイムセール

解法


幅優先でやった。
「これ絶対いいやろ(確信)」と思って提出したソースが4回くらい違って何度も絶望した問題だった。
大きな原因としては売り切れる時刻までを買える時刻に含んでいたこと。察せない。
以下ソース42行目。>=を>にずっとしていた。さらに察せない。
気づけてよかった。

後から他の人の解法見ると深さで実装してて実装量少ないし良いなあとは思った。
探索≒bfsに自分の頭の中で脳内変換されてしまう。早いから(ry(言い訳)

ソース


#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<cctype>
using namespace std;
typedef pair< int , int > Pt;
typedef pair< pair<int , Pt > , Pt > P; // Pt(cost,bit) , Pt(y,x)
#define fr first
#define sc second
#define INF ( 1 << 30 )
const int dx[] = { 0, 1, -1, 0}, dy[] = { 1, 0, 0, -1};
struct edge{
  int prime,st,ed;
  edge(){};
  edge(int prime,int st,int ed):prime(prime),st(st),ed(ed){}
};
edge info[11];
int x, y;
char mas[21][21];
bool no_over(int ny,int nx){
  return ny >= 0 && ny < y && nx >= 0 && nx < x;
}
int bfs(Pt& start){
  int ret = 0;
  bool used[21][21][1<<11];
  fill_n(used[0][0],20*20*(1<<11),false);
  queue< P >que;
  que.push(P(make_pair(0,Pt((1<<10)-1,0)),start));
  used[start.fr][start.sc][(1<<10)-1] = true;
  while(!que.empty()){
    P p = que.front();
    que.pop();
    for(int i = 0 ; i < 4 ; i++ ){
      int ny = p.sc.fr + dy[i], nx = p.sc.sc + dx[i];
      if(!no_over(ny,nx)) continue;
      if(isdigit(mas[ny][nx])){
        edge e = info[mas[ny][nx]-'0'];
        int no = mas[ny][nx] - '0';
        ny = p.sc.fr, nx = p.sc.sc;
        if( !(p.fr.sc.fr & (1 << no)) || p.fr.fr >= e.ed) continue;
        if(p.fr.fr >= e.st){
          ret = max(ret,p.fr.sc.sc + e.prime);
          if(used[ny][nx][p.fr.sc.fr&~(1<<no)]++) continue;
          que.push(P(make_pair(p.fr.fr,Pt(p.fr.sc.fr&~(1<<no),p.fr.sc.sc + e.prime)),Pt(ny,nx)));
        }else que.push(P(make_pair(e.st,Pt(p.fr.sc.fr,p.fr.sc.sc)),Pt(ny,nx)));
      }else{
        if(used[ny][nx][p.fr.sc.fr]++) continue;
        que.push(P(make_pair(p.fr.fr+1,Pt(p.fr.sc.fr,p.fr.sc.sc)),Pt(ny,nx)));
      }
    }
  }
  return ret;
}
 
int main(){
  while(cin >> x >> y , x){
    Pt st;
    for(int i = 0 ; i < y ; i++ ){
      for(int j = 0 ; j < x ; j++ ){
        cin >> mas[i][j];
        if(mas[i][j] == 'P') st = Pt(i,j);
      }
    }
    int n;
    cin >> n;
    for(int i = 0 ; i < n ; i++ ){
      int g,d,s,e;
      cin >> g >> d >> s >> e;
      info[g] = edge(d,s,e);
    }
    cout << bfs(st) << endl;
  }
}

2013年11月17日日曜日

AOJ1166 迷図と命ず

解法


入力の処理に一工夫必要なやつ。
四次元配列作って、辺で判定してやったら勝てた。
あとは幅やっただけ。Dijkstraしか最近書いてないので,ちょっと忘れてた。

ソース


#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
#define INF ( 1 << 30 )
#define fr first
#define sc second
typedef pair< int , int > Pt;
const int dx[] = { 0, 1, -1, 0}, dy[] = { 1, 0, 0, -1};
int w, h, cost[30][30];
bool mas[30][30][30][30];
int bfs(){
  fill_n(cost[0],900,0);
  queue<Pt> que;
  cost[0][0] = 1;
  que.push(Pt(0,0));
  while(!que.empty()){
    Pt p = que.front();
    que.pop();
    if(p.fr == h - 1 && p.sc == w - 1) break;
    for(int i = 0 ; i < 4 ; i++ ){
      int ny = p.fr + dy[i], nx = p.sc + dx[i];
      if(ny >= 0 && ny < h && nx >= 0 && nx < w && !cost[ny][nx]
         && !mas[p.fr][p.sc][ny][nx]  && !mas[ny][nx][p.fr][p.sc]){
        cost[ny][nx] = cost[p.fr][p.sc] + 1;
        que.push(Pt(ny,nx));
      }
    }
  }
  return cost[h-1][w-1];
}
int main(){
  while(cin >> w >> h , w){
    for(int i = 0 ; i < 2 * h - 1 ; i++ ){
      if(i % 2)
        for(int j = 0 ; j < w ; j++ ) cin >> mas[i/2][j][i/2+1][j];
      else
        for(int j = 0 ; j < w - 1 ; j++ ) cin >> mas[i/2][j][i/2][j+1];
    }
    cout << bfs() << endl;
  }
}

2013年11月16日土曜日

AOJ1240 Unreliable Message

問題概要


6人の公務員が王にメッセージを渡したかった。
だがしかし6人の公務員はメッセージを少し変えてしまう。
文字列を復元せよ。

J君:1つずつ左にすべての文字を回転させる。(例)aB23d→B23da
C君:1つずつ右にすべての文字を回転させる。(例)aB23d→daB23
E君:左半分と右半分の文字を入れ替える。文字数が奇数なら真ん中の文字はそのまま。
      (例)e3ac→ace3 aB23d→3d2aB
A君:メッセージを逆にする。(例)aB23d→d32Ba
P君:全ての数字に1を加える。但し9なら0になる。(例)aB23d→aB34d e9ac→e0ac
M君:全ての数字から1を引く。但し0なら9になる。(例)aB23d→aB12d e0ac→e9ac

解法


復元なので経由順を逆にして、変更時の動作も逆にする。

ソース


#include<iostream>
#include<string>
#include<algorithm>
#include<cctype>
using namespace std;
int main(){
  int n;
  cin >> n;
  while(n--){
    string done,message;
    cin >> done >> message;
    reverse(done.begin(),done.end());
    for(int i = 0 ; i < done.size() ; i++ ){
      switch(done[i]){
      case 'J':
        rotate(message.begin(),message.begin()+message.size()-1,message.end());
        break;
      case 'C':
        rotate(message.begin(),message.begin()+1,message.end());
        break;
      case 'E':
        for(int j = 0 ; j < message.size() / 2 ; j++ ){
          swap(message[j],message[(message.size()+1)/2+j]);
        }
        break;
      case 'A':
        reverse(message.begin(),message.end());
        break;
      case 'P':
        for(int j = 0 ; j < message.size() ; j++ ){
          if(isdigit(message[j])) message[j] = (message[j]-'0'+9)%10+'0';
        }
        break;
      case 'M':
        for(int j = 0 ; j < message.size() ; j++ ){
          if(isdigit(message[j])) message[j] = (message[j]-'0'+1)%10+'0';
        }
        break;
      }
    }
    cout << message << endl;
  }
}
rotate()を使いたかったからやった(小声)

2013年11月13日水曜日

AOJ0268 金剛の型

単に入力が16進数で与えられて、10進数に良い感じになおして出力するだけ。
学校でやって爆死したので、家でやったが爆死。合計8WA。
書いてくうちにシンプルになっていった。
ジャッジをデバックに使わないように(戒め)

#include<cmath>
#include<cstdio>
using namespace std;
typedef unsigned int ui;
int main(){
  int q;
  scanf("%d",&q);
  while(q--){
    ui bit;
    scanf("%x",&bit);
    if(bit >> 31 & 1) putchar('-');
    int Seisu = ( bit & 0x7FFFFFFF ) >> 7;
    int Syosu = ( bit & 0x0000007F );
    double ans = 0.0;
    int keta = 0;
    for(int j = 0 ; j < 7 ; j++){
      if(Syosu & (1 << (6-j))){
        ans += pow(0.5,j+1);
        keta = j;
      }
    }
    printf("%.*lf\n",keta+1,Seisu+ans);
  }
}

2013年11月12日火曜日

AOJ1150 崖登り

見た感じ得意分野の経路探索系だったのでやった。
見たとおりそのまま実装したらこうなった。

#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<vector>
using namespace std;
typedef pair< int , int > P;
typedef pair< pair< int , P > , pair< P , bool > > PP;
//コスト,左座標,右座標,次どっちの足?(true:左,false:右)
#define fr first
#define sc second
#define MAX_W 30
#define MAX_H 60
#define mp(a,b) make_pair(a,b)
 
const int dx[]={1,1,1,1,1,2,2,2,3},dy[]={2,1,0,-1,-2,1,0,-1,0};
int w,h;
char mas[MAX_H][MAX_W];
 
bool over(int nx,int ny){
  return nx < 0 || nx >= w || ny < 0 || ny >= h;
}
 
int bfs(vector<P>& st){
  map< pair< pair< P , P > , bool > , bool > used;
  priority_queue< PP , vector<PP> , greater<PP> > que;
  for(int i = 0 ; i < st.size() ; i++ ){
    que.push(PP(mp(0,st[i]),mp(P(-1,-1),false)));
    que.push(PP(mp(0,P(-1,-1)),mp(st[i],true)));
  }
  while(!que.empty()){
    PP p = que.top();
    que.pop();
    if(used[mp(mp(p.fr.sc,p.sc.fr),p.sc.sc)]++) continue;
    if(mas[p.fr.sc.fr][p.fr.sc.sc] == '0') return p.fr.fr;
    if(mas[p.sc.fr.fr][p.sc.fr.sc] == '0') return p.fr.fr;
    for(int i = 0 ; i < 9 ; i++ ){
      if(p.sc.sc){ // 左足を動かす
        int ny = p.sc.fr.fr + dy[i] , nx = p.sc.fr.sc - dx[i];
        if(over(nx,ny) || mas[ny][nx] == 'X') continue;
        que.push(PP(mp(p.fr.fr+(mas[ny][nx]-'0'),P(ny,nx)),mp(p.sc.fr,false)));
      }else{ //右足を動かす
        int ny = p.fr.sc.fr + dy[i] , nx = p.fr.sc.sc + dx[i]; // p.sc.fr.sc + dx[i]
        if(over(nx,ny) || mas[ny][nx] == 'X') continue;
        que.push(PP(mp(p.fr.fr+(mas[ny][nx]-'0'),p.fr.sc),mp(P(ny,nx),true)));
      }
    }
  }
  return -1;
}
 
int main(){
  while(cin >> w >> h , w){
    vector<P> st;
    for(int i = 0 ; i < h ; i++ ){
      for(int j = 0 ; j < w ; j++ ){
        cin >> mas[i][j];
        if(mas[i][j] == 'S') st.push_back(P(i,j));
        if(mas[i][j] == 'T') mas[i][j] = '0';
      }
    }
    cout << bfs(st) << endl;
  }
}
ダイクストラバグったじゃんと思ったら、入力のミスだったという経緯があって、
精神的に逝きました。(小並感)。59,60行目がmas[i][w]になっていたというクソザコ。
で、解き終わったあと、どっちかの足だけでいいじゃんということに気付いて絶望。
気分が悪いので解き直し。
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<vector>
using namespace std;
typedef pair< int , int > P;
typedef pair< pair< int , P > , bool > PP;
//コスト,座標,次どっちの足?(true:左,false:右)
#define fr first
#define sc second
#define MAX_W 30
#define MAX_H 60
#define mp(a,b) make_pair(a,b)
 
const int dx[]={1,1,1,1,1,2,2,2,3},dy[]={2,1,0,-1,-2,1,0,-1,0};
int w,h;
char mas[MAX_H][MAX_W];
 
bool over(int nx,int ny){
  return nx < 0 || nx >= w || ny < 0 || ny >= h;
}
 
int bfs(vector<P>& st){
  bool used[MAX_H][MAX_W][2] = {};
  priority_queue< PP , vector<PP> , greater<PP> > que;
  for(int i = 0 ; i < st.size() ; i++ ){
    que.push(PP(mp(0,st[i]),false));
    que.push(PP(mp(0,st[i]),true));
  }
  while(!que.empty()){
    PP p = que.top();
    que.pop();
    if(used[p.fr.sc.fr][p.fr.sc.sc][p.sc]++) continue;
    if(mas[p.fr.sc.fr][p.fr.sc.sc] == '0') return p.fr.fr;
    for(int i = 0 ; i < 9 ; i++ ){
      int ny = p.fr.sc.fr + dy[i] , nx = p.fr.sc.sc + (p.sc ? dx[i] : -dx[i]);
      if(over(nx,ny) || mas[ny][nx] == 'X') continue;
      que.push(PP(mp(p.fr.fr+(mas[ny][nx]-'0'),P(ny,nx)),!p.sc));
    }
  }
  return -1;
}
 
int main(){
  while(cin >> w >> h , w){
    vector<P> st;
    for(int i = 0 ; i < h ; i++ ){
      for(int j = 0 ; j < w ; j++ ){
        cin >> mas[i][j];
        if(mas[i][j] == 'S') st.push_back(P(i,j));
        if(mas[i][j] == 'T') mas[i][j] = '0';
      }
    }
    cout << bfs(st) << endl;
  }
}
疲れた。

2013年11月11日月曜日

JOI予選2007

1.おつり AOJ0521

#include<iostream>
#include<algorithm>
using namespace std;
#define INF ( 1 << 30 )
int main(){
  int dp[1000],tmp[]={500,100,50,10,5,1},n;
  fill_n(dp,1000,INF);
  dp[0] = 0;
  for(int i = 0 ; i < 1000 ; i++ ){
    for(int j = 0 ; j < 6 ; j++ ){
      if(i - tmp[j] >= 0) dp[i] = min(dp[i],dp[i-tmp[j]]+1);
    }
  }
  while(cin >> n , n) cout << dp[1000-n] << endl;
}
なんかDPで解きたくなったので解いた。

2.JOI and IOI AOJ0522

#include<iostream>
#include<string>
using namespace std;
int main(){
  string s;
  while(cin >> s){
    int joi = 0, ioi = 0;
    for(int i = 2 ; i < s.size() ; i++ ){
      if(s.substr(i-2,3) == "JOI") joi++;
      else if(s.substr(i-2,3) == "IOI") ioi++;
    }
    cout << joi << endl << ioi << endl;
  }
}
substrで良い感じに抜き出してやった。

問題3.カードゲーム AOJ0523

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
int main(){
  int n, use[201], now, ban;
  while(cin >> n , n){
    priority_queue<int,vector<int>,greater<int> > TaHa[2];
    queue<int> tmp;
    fill_n(use,201,false),now = 0,ban = 0;
    for(int i = 0 ; i < n ; i++ ){
      int card;
      cin >> card;
      use[card] = true;
      TaHa[0].push(card);
    }
    for(int i = 1 ; i <= 2*n ; i++) if(!use[i]) TaHa[1].push(i);
    while(!TaHa[0].empty() && !TaHa[1].empty()){
      while(!TaHa[ban].empty() && now >= TaHa[ban].top()){
        tmp.push(TaHa[ban].top());
        TaHa[ban].pop();
      }
      if(!TaHa[ban].empty()) now = TaHa[ban].top() , TaHa[ban].pop();
      else now = 0;
      while(!tmp.empty()) TaHa[ban].push(tmp.front()) , tmp.pop();
      ban ^= 1;
    }
    cout << TaHa[1].size() << endl << TaHa[0].size() << endl;
  }
}
優先度付きキューで解いた。デフォルトが降順になっていることに気付かずバグらせた。
priority_queueは降順。覚えたい(戒め)

問題4.星座探し AOJ0524

#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
#define fr first
#define sc second
typedef pair< int , int > Pt;
int m, n;
bool serach( Pt *a, Pt *b, int& x,int& y){
  for(int j = 1 ; j < m ; j++ ){
    if(!binary_search(b,b+n,Pt(a[j].fr+x,a[j].sc+y))) return false;
  }
  return true;
}
int main(){
  while(cin >> m , m){
    Pt M[m];
    for(int i = 0 ; i < m ; i++ ) cin >> M[i].fr >> M[i].sc;
    cin >> n;
    Pt D[n];
    for(int i = 0 ; i < n ; i++ ) cin >> D[i].fr >> D[i].sc;
    sort(D,D+n);
    for(int i = 0 ; i < n ; i++ ){
      int x = D[i].fr - M[0].fr , y = D[i].sc - M[0].sc;
      if(serach(M,D,x,y)){
        cout << x << " " << y << endl;
        break;
      }
    }
  }
}
2分探索すれば早く探せるんじゃね的な考え方。
STL用意されてて良い。

問題5.おせんべい AOJ0525

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
  int h,w;
  bool mas[10][10000];
  while( cin >> h >> w , h ){
    for(int i = 0 ; i < h ; i++ ){
      for(int j = 0 ; j < w ; j++ ){
        cin >> mas[i][j];
      }
    }
    int ans = 0;
    for(int i = 0 ; i < (1 << h) ; i++ ){
      int sum = 0;
      for(int j = 0 ; j < w ; j++ ){
        int cnt = 0;
        for(int k = 0 ; k < h ; k++ ){
          cnt += mas[k][j]^(i>>k&1);
        }
        sum += max( h - cnt , cnt );
      }
      ans = max( sum , ans );
    }
    cout << ans << endl;
  }
}
行の制約10じゃん。やったね!
裏返すパターンを全通り試すしか無い。ちょっと端折った。

問題6.船旅 AOJ0526

#include<iostream>
#include<algorithm>
using namespace std;
#define INF ( 1 << 26 )
int main(){
  int n,m,DATA[101][101];
  while(cin >> n >> m , n){
    fill_n(DATA[0],101 * 101,INF);
    for(int i = 0 ; i < 101 ; i++ ) DATA[i][i] = 0;
    while(m--){
      int s, a, b, c;
      cin >> s >> a >> b;
      if(s){
        cin >> c;
        if(DATA[a][b] > c){
          DATA[a][b] = DATA[b][a] = c;
          for(int j = 1 ; j <= n ; j++){
            for(int k = 1 ; k <= n ; k++){
              DATA[j][k] = min(DATA[j][k] , DATA[j][a] + DATA[a][k]);
              DATA[j][k] = DATA[k][j] = min(DATA[j][k] , DATA[j][b] + DATA[b][k]);
            }
          }
        }
      }else cout << (DATA[a][b] == INF ? -1 : DATA[a][b]) << endl;
    }
  }
}
普通にWFやるといっちゃうので更新された所だけを更新する。O(n^2)くらいに出来る。

2013年11月10日日曜日

多倍長なヤツ

何か、あると便利じゃね?という妄想から実装しました。


足し算


string TASHIZAN(string a,string b){
  int maxlen = max(a.size(),b.size()) + 1;
  vector<int> ret(maxlen,0);
  string ans;
  reverse(a.begin(),a.end()), reverse(b.begin(),b.end());
  for(int i = 0 ; i < a.size() ; i++ ) ret[i] = a[i] - '0';
  for(int i = 0 ; i < b.size() ; i++ ) ret[i] += b[i] - '0';
  for(int i = 0 ; i < maxlen - 1 ; i++ ){
    ans += (ret[i] % 10) + '0';
    ret[i+1] += ret[i] / 10;
  }
  if(ret[maxlen-1]) ans += ret[maxlen-1] + '0';
  reverse(ans.begin(),ans.end());
  return ans;
}

引き算(マイナス判定も入れた)


bool Witch_big(string& a, string& b){
  if(a == b) return true;
  if(a.size() > b.size()) return true;
  else if(a.size() < b.size()) return false;
  else return a > b;
}
string HIKIZAN(string a,string b){
  bool minus = false;
  int maxlen = max(a.size(),b.size()), cnt = 0;
  vector<int> ret(maxlen,0);
  string ans;
  if(!Witch_big(a,b)) swap(a,b), minus = true;
  reverse(a.begin(),a.end()), reverse(b.begin(),b.end());
  for(int i = 0 ; i < a.size() ; i++) ret[i] = a[i] - '0';
  for(int i = 0 ; i < b.size() ; i++) ret[i] -= b[i] - '0';
  for(int i = 0 ; i < maxlen ; i++){
    while(ret[i] < 0) ret[i] += 10, ret[i+1]--;
    ans += ret[i] + '0';
  }
  reverse(ans.begin(),ans.end());
  while(ans[cnt] == '0' && cnt != ans.size() - 1) cnt++;
  return (minus ? "-" : "" ) + ans.substr(cnt);
}

掛け算


string KAKEZAN(string a,string b){
  int maxlen = a.size() + b.size(), cnt = 0;
  vector<int> ret(maxlen+1,0);
  string ans;
  reverse(a.begin(),a.end()), reverse(b.begin(),b.end());
  for(int i = 0 ; i < a.size() ; i++) for(int j = 0 ; j < b.size() ; j++){
      ret[i+j] += (a[i]-'0') * (b[j]-'0');
    }
  for(int i = 0 ; i < maxlen ; i++){
    ans += (ret[i] % 10) + '0';
    ret[i+1] += ret[i] / 10;
  }
  reverse(ans.begin(),ans.end());
  while(ans[cnt] == '0') cnt++;
  return ans.substr(cnt);
}

除算は面倒臭かった()

2013年11月9日土曜日

パソコン甲子園(PCK)2013 もう一つの本戦 参加記

予選に落ちた後悔を引きずりながら先輩と参加。

4AC+0WAでした。
予選は6AC+4WA。僕の緊張が極限に達して自滅しました。

1→5→2→3の順でAC。3のAC時刻が17:20:09とかなっていたので更新されないじゃんとなって萎えました。
ちょっと本番と変えてるけど、ソース。

テンプレート


用意してなかったので先輩のテンプレートをコピペしました。若干省略。
#include<iostream>
#include<string>
#include<bitset>
#include<cctype>
#include<cstdlib>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<complex>
using namespace std;
 
// structure
typedef long long ll;
typedef pair < int , int > Pi;
typedef pair < int , Pi > Pii;
 
#define INF (1 << 28)
#define SQR(x) ((x)*(x))
 
//repetition
#define reps(i,j,n) for(int i = j ; i < n ; ++i)
#define rep(i, n) reps(i,0,n)
#define EACH(i,c) for(__typeof((c).begin()) ;i=(c).begin(); i!=(c).end(); ++i)
 
// containar
#define ALL(v) (v).begin(), (v).end()
#define RALL(a) (a).rbegin(), (a).rend()
#define sz(z) (int)((z).size())
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define UNQ(s) {sort(ALL(s));(s).erase(unique(ALL(s)),(s).end());}
#define fr first
#define sc second

// PROG START

int main(){

}

問題1.テニス(6点)


パッと見た感じ再帰しか浮かばなかったので再帰で解きました。 答えはvectorで持たせました。
int A,B;
vector<string> ans;
void solve(int a,int b,string t){
  if(a == A && b == B){
    ans.pb(t);
    return;
  }
  if(a > 6 || b > 6) return;
  if(a == 5 && b == 5) return;
  if((a == 5 && b < 4) || (a < 4 && b == 5)) return;
  if((a == 6 && b == 4) || (a == 4 && b== 6)) return;
  solve(a+1,b,t+"A");
  solve(a,b+1,t+"B");
}
int main(){
  cin >> A >> B;
  solve(0,0,"");
  sort(ALL(ans));
  rep(i,ans.size()) cout << ans[i] << endl;
}


問題2.親方の給料計算(6点)


超難問。O(N*M*L) = O(10^10) で逝くじゃんと思って怖くて触れにくかった問題です。
TLE覚悟でソースを出してみたらACして,ファ??ってなりました。
正答率10%切ってて,下のソースを早くしようとしたけど,ちょっと察せなかった。
int main(){
  int N, M, a, b, c, L;
  cin >> N >> M;
  vector<Pi> vc[N];
  int cost[M];
  while(cin >> a >> b >> c , a + b + c){
    vc[a-1].push_back(Pi(b-1,c));
  }
  cin >> L;
  while(L--){
    rep(i,M) cin >> cost[i];
    rep(i,N){
      int sum = 0;
      rep(j,vc[i].size())
        sum += cost[vc[i][j].fr] * vc[i][j].sc;
      cout << (i ? " " : "") << sum;
    }
    cout << endl;
  }
}


問題3.塵劫記(6点)


みんなACしてて「コレ簡単?(困惑)」になっていた。多倍長乗算。
変換する部分は事前に先輩が実装してくれた。凄い。
あとは多倍長,書きたくなくて書かなかったが、仕方なくやったらすぐ実装できて良かった。
string lis[] = {
  "","Man","Oku","Cho","Kei","Gai","Jo","Jou","Ko","Kan",
  "Sei","Sai","Gok","Ggs","Asg","Nyt","Fks","Mts",
};
string KAKEZAN(string a,string b){
  int ans[100] = {};
  string rec;
  reverse(ALL(a));
  reverse(ALL(b));
  rep(i,a.size()) rep(j,b.size()){
    ans[i+j] += (a[i] - '0') * (b[j] - '0');
  }
  rep(i,99){
    ans[i+1] += ans[i] / 10;
    rec += (ans[i] % 10) + '0';
  }
  reverse(ALL(rec));
  int cnt = 0;
  while(rec[cnt] == '0') cnt++;
  return rec.substr(cnt);
}
void ZIN(string s){
  vector<string> v;
  string ret;
  reverse(ALL(s));
  for(int i = 0 ; i < (int)s.size() ; i += 4){
    string cut = s.substr(i,min(4,sz(s)-i));
    string Class = lis[i/4];
    reverse(ALL(cut));
    int pos = -1;
    rep(j,cut.size()){
      if(cut[j] != '0'){ pos = j; break; };
    }
    if(pos == -1) continue;
    string in;
    reps(j,pos,cut.size()) in += cut[j];
    v.pb(in+Class);
  }
  reverse(ALL(v));
  rep(i,v.size()) cout << v[i];
  cout << endl;
}
int main(){
  string a;
  int b;
  while(cin >> a >> b , a != "0" || b != 0){
    string ret = "1";
    rep(i,b) ret = KAKEZAN(ret,a);
    ZIN(ret);
  }
}

問題5.無限急行(8点)


先輩が他の問題を解読しているうちに僕が実装。
一発ACするとは思っていなかった。
たくさん関数分けて,たくさんビットシフトすればいいんじゃね的な考え方で
バグってもいいので適当に書いた。十分このソースも汚いけど、本番はそれ以上に汚かった。
int s, d;
bool check(int train,int n){
  if(!train) return true;
  int shift = pow(2,train);
  if(n + shift > d) return false;
  rep(i,32){
    if(abs(n) & (1<<i)){
      if(i && (1<<i) % shift == 0) return true;
      else return false;
    }
  }
  return true;
}
int cnt(int no){
  int i = 0;
  while(check(i,no)) i++;
  return i - 1;
}
int solve(int now, int cost){
  if(now == d) return cost;
  int train = cnt(now);
  if(now + pow(2,train) <= d){
    return solve(now+pow(2,train),cost+1);
  }else{
    return solve(now,cost+1);
  }
}
int main(){
  int N;
  cin >> N;
  while(N--){
    cin >> s >> d;
    cout << solve(s,0) << endl;
  }
}
5問目と3問目解けた高揚感で4問目サボってしまったのが反省点。 ソートして再帰すればHappyそうなので今度やって挙げます。

11/12追記
、と思ったけど先輩が解いてくれてたのでやめました。
http://hayashiya.hatenablog.com/entry/2013/11/12/205702

2013年11月7日木曜日

JOI予選2006

1.得点 AOJ0510

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
  int A = 0, B = 0, in;
  for(int i = 0; i < 4; i++, A += in) cin >> in;
  for(int i = 0; i < 4; i++, B += in) cin >> in;
  cout << max( A, B) << endl;
}
足して比べるだけ(小並感)


2.未提出者は誰だ

#include<iostream>
using namespace std;
int main(){
  bool ok[31] = {};
  for(int i = 0, in; i < 28; i++){
    cin >> in;
    ok[in] = true;
  }
  for(int i = 1; i < 30; i++){
    if(!ok[i]) cout << i << endl;
  }
}
やるだけ。


シーザー暗号

#include<iostream>
#include<string>
using namespace std;
int main(){
  string str;
  cin >> str;
  for(int i = 0; i < str.size(); i++){
    cout << (char)((str[i] - 'A' + 23) % 26 + 'A');
  }
  cout << endl;
}
ちょっと迷ったけど気にせず出力。


カードの並べ替え

#include<iostream>
using namespace std;
int main(){
  int n, m, k, card[200], next[200];
  cin >> n >> m;
  for(int i = 0; i < 2*n; i++) card[i] = i + 1;
  while(m--){
    cin >> k;
    if(!k){ //リシャッフル
      for(int i = 0; i < n; i++){
        next[i*2] = card[i];
        next[i*2+1] = card[n+i];
      }
    }else{ //カット
      for(int i = 0; i < k; i++) next[i+2*n-k] = card[i];
      for(int i = k; i < 2*n; i++) next[i-k] = card[i];
    }
    for(int i = 0; i < 2*n; i++) card[i] = next[i];
  }
  for(int i = 0; i < 2*n; i++) cout << card[i] << endl;
}
眠くなってきたのでちょっとバグらせた。やるだけ。


品質検査

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
  int a, b, c, N , data[1000][4], flg[301] , cnt;

  while(cin >> a >> b >> c , a+b+c){
    fill_n(flg,301,2), cnt = 0;
    cin >> N;
    for(int i = 0; i < N; i++, cnt++){
      for(int j = 0; j < 4; j++) cin >> data[cnt][j];
      if(data[cnt][3]){
        for(int j = 0; j < 3; j++) flg[data[cnt][j]] = 1;
        cnt--;
      }
    }
    for(int i = 0; i < cnt; i++) for(int j = 0; j < 3; j++){
        if(flg[data[i][j]] == 1 && flg[data[i][(j+1)%3]] == 1){
          flg[data[i][(j+2)%3]] = 0;
        }
      }
    for(int i = 1; i <= a+b+c; i++) cout << flg[i] << endl;
  }
}
貪欲法。2つ正常ならもう1つは故障。
寝る。