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つは故障。
寝る。