「AOJ521~530」の編集履歴(バックアップ)一覧はこちら

AOJ521~530」(2011/10/30 (日) 17:08:08) の最新版変更点

追加された行は緑色になります。

削除された行は赤色になります。

---- *0521 Change 釣銭枚数を計算する問題。 解法 書くだけです。 #include<stdio.h> int main(){ int n; while(1){ scanf("%d",&n); if(n==0)break; n=1000-n; printf("%d\n",n/500+(n%500)/100+(n%100)/50+(n%50)/10+(n%10)/5+(n%5)); } } ---- *0522 JOI and IOI http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0522 文字列の中からJOIとIOIの数を数える問題。 解法 文字列を前から見てカウントするだけです。 この問題では単語が短いので手抜きします。 #include <stdio.h> int main(){ char text[10001]; while(scanf("%s",text)!=EOF){ int ioi=0,joi=0; for(int i=0;text[i+2]!='\0';i++){ if(text[i+1]=='O' && text[i+2]=='I'){ if(text[i]=='J')joi++; if(text[i]=='I')ioi++; } } printf("%d\n%d\n",joi,ioi); } } ---- *0523 Card Game http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0523 解法 計算量が低いうえおそらくNP困難問題なので愚直に実装します。 もしNが大きくなった場合どうするか、少し考えつきません。 #include <stdio.h> #include <string.h> void calc(int n){ int nowCard; bool cards[2][201]; memset(cards[0],false,201); memset(cards[1],true,201); for(int i=0;i<n;i++){ scanf("%d",&nowCard); cards[0][nowCard]=true; cards[1][nowCard]=false; } int p,pturn=0,ans[2]; ans[0]=ans[1]=n; while(1){ p=1; while(p<=2*n){ if(cards[pturn][p]){ cards[pturn][p]=false; p++; ans[pturn]--; if(ans[pturn]==0)break; pturn=(pturn+1)%2; }else{ p++; } } if(ans[pturn]==0)break; pturn=(pturn+1)%2; } printf("%d\n%d\n",ans[1],ans[0]); } int main(){ int n; while(1){ scanf("%d",&n); if(n==0)break; calc(n); } } ---- *0524 Searching Constellation http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0524 平面上の星の座標データから星座を探す問題。 解法 一つずつ平行移動して重なるかチェックします。 星のポイントが整数であることを利用し、星の座標をsetに入れておくことで計算量をmlog(m)に下げます。 #include<stdio.h> #include<set> struct point{ int x,y; bool operator<(const point p)const{ if(x!=p.x)return x<p.x; return y<p.y; } }; void search(int m){ point p; int xs[201],ys[201]; for(int i=0;i<m;i++){ scanf("%d %d",&xs[i],&ys[i]); } int n; scanf("%d",&n); std::set<point> allPs; std::set<point>::iterator it; for(int i=0;i<n;i++){ scanf("%d %d",&p.x,&p.y); allPs.insert(p); } it=allPs.begin(); bool allOK; int sx,sy; while(it!=allPs.end()){ sx=(*it).x-xs[0]; sy=(*it).y-ys[0]; allOK=true; for(int i=0;i<m;i++){ p.x=sx+xs[i]; p.y=sy+ys[i]; if(allPs.find(p)==allPs.end()){ allOK=false; break; } } if(allOK==true){ printf("%d %d\n",sx,sy); return; } it++; } } int main(){ int m; while(1){ scanf("%d",&m); if(m==0) break; search(m); } } ---- *0525 Osenbei http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0525 おせんべいをひっくり返してきちんと焼ける数を数える問題。 解法 横列の裏返しは2^10通りしかなく、一度横列の裏表が決まれば最大化する方法は一通りしかないことを利用して解きます。 高速化する方法があるようですが私には思いつきませんでした。 #include<stdio.h> #include<string.h> int map[11][10001]; int remap[11][10001]; int ans; int c,r; void saiki(int deep){ if(deep==r){ int count=0,temp; for(int x=0;x<c;x++){ temp=0; for(int y=0;y<r;y++){ temp+=remap[y][x]; } temp=temp>r-temp?temp:r-temp; count+=temp; } ans=ans>count?ans:count; }else{ memcpy(remap[deep],map[deep],4*10001); saiki(deep+1); for(int x=0;x<c;x++)remap[deep][x]=(map[deep][x]+1)%2; saiki(deep+1); } } void calc(){ ans=0; for(int y=0;y<r;y++){ for(int x=0;x<c;x++){ scanf("%d",&map[y][x]); } } saiki(0); printf("%d\n",ans); } int main(){ while(1){ scanf("%d %d",&r,&c); if(c==0 && r==0) break; calc(); } } ---- *0526 Boat Travel http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0526 島から島へ渡る船舶の航路情報をもとに目的の島まで行く最小コストを求める問題です。 入力データの途中で航路情報が更新されるので、入力とともに航路データが変化します。 最短航路の更新作業を素早い実装にする必要があります。 解法 この問題最初航路データの更新をダイクストラ法を使って行いました。 当ページ掲載にあたりよりよいコード掲載のためにリンク先を基準にコードを書きなおしました。 http://www.ioi-jp.org/joi/2007/2008-yo-prob_and_sol/2008-yo-t6/review/2008-yo-t6-review.html #include<stdio.h> const int size=101; const int max=100000000; int cost[size][size]; int n,k; void costUpData(int c,int d,int e){ if(cost[c][d]<=e)return; int sum,sum2; cost[c][d]=cost[d][c]=e; for(int s=1;s<=n;s++){ for(int g=1;g<=n;g++){ sum=cost[s][c]+e+cost[d][g]; if(sum<cost[s][g]) cost[s][g]=cost[g][s]=sum; sum=cost[s][d]+e+cost[c][g]; if(sum<cost[s][g]) cost[s][g]=cost[g][s]=sum; } } } void calc(){ for(int i=0;i<=n;i++){ for(int j=0;j<=n;j++){ cost[i][j]=max; if(i==j)cost[i][j]=0; } } int a,b,type,c,d,e; for(int i=0;i<k;i++){ scanf("%d",&type); if(type==0){ scanf("%d %d",&a,&b); printf("%d\n",cost[a][b]==max?-1:cost[a][b]); }else{ scanf("%d %d %d",&c,&d,&e); costUpData(c,d,e); } } } int main(){ while(1){ scanf("%d %d",&n,&k); if(n==0 &&k==0) break; calc(); } } ---- *0527 Setting Go Stones http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0527 碁石の置き換え問題。 解法 連続して同色の碁石が並んでいる部分を塊で扱います。 #include<stdio.h> #include<vector> struct stone{ int count,color; }; void calc(int n){ std::vector<stone> stones; stone s; int color,last; for(int i=1;i<=n;i++){ scanf("%d",&s.color); s.count=1; last=stones.size()-1; if(i%2==1){ if(stones.empty()){ stones.push_back(s); }else if(stones[last].color==s.color){ stones[last].count++; }else{ stones.push_back(s); } }else{ color=stones[last].color; if(color==s.color){ stones[last].count++; }else{ s=stones[last]; stones.pop_back(); if(stones.empty()){ s.color=(s.color+1)%2; s.count++; stones.push_back(s); }else{ stones[last-1].count+=s.count+1; } } } } int ans=0; for(int i=0;i<stones.size();i++){ if(stones[i].color==0)ans+=stones[i].count; } printf("%d\n",ans); } int main(){ int n; while(1){ scanf("%d",&n); if(n==0) break; calc(n); } } ---- *0528 Common Sub-String http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0528 最長一致部分文字列を探す問題。 解法 動的計画法でn^2の計算量で解きます。 #include<stdio.h> #include<string.h> int main(){ char text[4001],text2[4001]; int memo[2][4003],now,old,ans; while(scanf("%s",text)!=EOF){ memset(memo,0,sizeof(memo)); scanf("%s",text2); ans=0; for(int i=0;text2[i]!='\0';i++){ now=i%2; old=(i+1)%2; memset(memo[now],0,4003*4); for(int j=0;text[j]!='\0';j++){ if(text2[i]==text[j]){ memo[now][j+1]=memo[old][j]+1; ans=ans>memo[now][j+1]?ans:memo[now][j+1]; } } } printf("%d\n",ans); } } ---- *0529 Darts http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0529 ダーツを4回投げたときMを超えない可能な最高得点を探す問題。 解法 きれいなコードを用意できませんでした。 1本投げた場合を求め、次に2本投げた場合の全組み合わせを求め、Vectorに保存しておきます。 後は3本の組を1本のセットと2本のセットからバイナリサーチで最高点を求め、4本は2本+2本をバイナリサーチで求めます。 正答者データを見る限りこの問題は下記コードの半分程度のコード量で正答できさらなる高速化も可能なようです。 正答者データ一覧↓ http://judge.u-aizu.ac.jp/onlinejudge/problem_statistics.jsp?id=0529 #include <stdio.h> #include <vector> #include <algorithm> int solve(int n,int m); int main() { int n,m,ans; scanf("%d %d",&n,&m); while(n!=0 && m!=0){ ans=solve(n,m); printf("%d\n",ans); scanf("%d %d",&n,&m); } } int bsearch(std::vector<int> &v,int target){ int l=0,r=v.size(),size=v.size()-1; if(v[0]>target) return 0; if(v[size]<=target) return v[size]; int index; while(1){ index=(l+r)/2; if(v[index]==target) return target; if(index<size && v[index]<target && v[index+1]>target) return v[index]; if(v[index]>target){ r=index; continue; } if(v[index]<target){ l=index+1; continue; } } return -1; } int solve(int n,int m) { std::vector<int> wPoints; std::vector<int>::iterator it; int t; int datas[1001]; int p=0; int ans=0; //一本だけ使う場合 for(int i=0;i<n;i++) { scanf("%d",&t); if(t==m){ return m; }else if(t<m){ datas[p]=t; p++; ans=std::max(ans,t); } } //2本使う場合 for(int i=0;i<p;i++){ for(int j=0;j<=i;j++){ t=datas[i]+datas[j]; if(t==m){ return m; }else if(t<m){ wPoints.push_back(t); ans=std::max(ans,t); } } } std::sort(wPoints.begin(),wPoints.end()); //3本使う場合 for(int i=0;i<p;i++){ t=m-datas[i]; t=datas[i]+bsearch(wPoints,t); ans=std::max(ans,t); if(ans==m) return m; } //4本使う場合 it=wPoints.begin(); while(it!=wPoints.end()) { t=m-(*it); //if(t<(*it)) return ans; t=(*it)+bsearch(wPoints,t); ans=std::max(ans,t); if(ans==m) return m; it++; } return ans; }
---- *0521 Change 釣銭枚数を計算する問題。 解法 書くだけです。 #include<stdio.h> int main(){ int n; while(1){ scanf("%d",&n); if(n==0)break; n=1000-n; printf("%d\n",n/500+(n%500)/100+(n%100)/50+(n%50)/10+(n%10)/5+(n%5)); } } ---- *0522 JOI and IOI http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0522 文字列の中からJOIとIOIの数を数える問題。 解法 文字列を前から見てカウントするだけです。 この問題では単語が短いので手抜きします。 #include <stdio.h> int main(){ char text[10001]; while(scanf("%s",text)!=EOF){ int ioi=0,joi=0; for(int i=0;text[i+2]!='\0';i++){ if(text[i+1]=='O' && text[i+2]=='I'){ if(text[i]=='J')joi++; if(text[i]=='I')ioi++; } } printf("%d\n%d\n",joi,ioi); } } ---- *0523 Card Game http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0523 解法 計算量が低いうえおそらくNP困難問題なので愚直に実装します。 もしNが大きくなった場合どうするか、少し考えつきません。 #include <stdio.h> #include <string.h> void calc(int n){ int nowCard; bool cards[2][201]; memset(cards[0],false,201); memset(cards[1],true,201); for(int i=0;i<n;i++){ scanf("%d",&nowCard); cards[0][nowCard]=true; cards[1][nowCard]=false; } int p,pturn=0,ans[2]; ans[0]=ans[1]=n; while(1){ p=1; while(p<=2*n){ if(cards[pturn][p]){ cards[pturn][p]=false; p++; ans[pturn]--; if(ans[pturn]==0)break; pturn=(pturn+1)%2; }else{ p++; } } if(ans[pturn]==0)break; pturn=(pturn+1)%2; } printf("%d\n%d\n",ans[1],ans[0]); } int main(){ int n; while(1){ scanf("%d",&n); if(n==0)break; calc(n); } } ---- *0524 Searching Constellation http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0524 平面上の星の座標データから星座を探す問題。 解法 一つずつ平行移動して重なるかチェックします。 星のポイントが整数であることを利用し、星の座標をsetに入れておくことで計算量をmlog(m)に下げます。 #include<stdio.h> #include<set> struct point{ int x,y; bool operator<(const point p)const{ if(x!=p.x)return x<p.x; return y<p.y; } }; void search(int m){ point p; int xs[201],ys[201]; for(int i=0;i<m;i++){ scanf("%d %d",&xs[i],&ys[i]); } int n; scanf("%d",&n); std::set<point> allPs; std::set<point>::iterator it; for(int i=0;i<n;i++){ scanf("%d %d",&p.x,&p.y); allPs.insert(p); } it=allPs.begin(); bool allOK; int sx,sy; while(it!=allPs.end()){ sx=(*it).x-xs[0]; sy=(*it).y-ys[0]; allOK=true; for(int i=0;i<m;i++){ p.x=sx+xs[i]; p.y=sy+ys[i]; if(allPs.find(p)==allPs.end()){ allOK=false; break; } } if(allOK==true){ printf("%d %d\n",sx,sy); return; } it++; } } int main(){ int m; while(1){ scanf("%d",&m); if(m==0) break; search(m); } } ---- *0525 Osenbei http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0525 おせんべいをひっくり返してきちんと焼ける数を数える問題。 解法 横列の裏返しは2^10通りしかなく、一度横列の裏表が決まれば最大化する方法は一通りしかないことを利用して解きます。 高速化する方法があるようですが私には思いつきませんでした。 #include<stdio.h> #include<string.h> int map[11][10001]; int remap[11][10001]; int ans; int c,r; void saiki(int deep){ if(deep==r){ int count=0,temp; for(int x=0;x<c;x++){ temp=0; for(int y=0;y<r;y++){ temp+=remap[y][x]; } temp=temp>r-temp?temp:r-temp; count+=temp; } ans=ans>count?ans:count; }else{ memcpy(remap[deep],map[deep],4*10001); saiki(deep+1); for(int x=0;x<c;x++)remap[deep][x]=(map[deep][x]+1)%2; saiki(deep+1); } } void calc(){ ans=0; for(int y=0;y<r;y++){ for(int x=0;x<c;x++){ scanf("%d",&map[y][x]); } } saiki(0); printf("%d\n",ans); } int main(){ while(1){ scanf("%d %d",&r,&c); if(c==0 && r==0) break; calc(); } } ---- *0526 Boat Travel http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0526 島から島へ渡る船舶の航路情報をもとに目的の島まで行く最小コストを求める問題です。 入力データの途中で航路情報が更新されるので、入力とともに航路データが変化します。 最短航路の更新作業を素早い実装にする必要があります。 解法 この問題最初航路データの更新をダイクストラ法を使って行いました。 当ページ掲載にあたりよりよいコード掲載のためにリンク先を基準にコードを書きなおしました。 http://www.ioi-jp.org/joi/2007/2008-yo-prob_and_sol/2008-yo-t6/review/2008-yo-t6-review.html #include<stdio.h> const int size=101; const int max=100000000; int cost[size][size]; int n,k; void costUpData(int c,int d,int e){ if(cost[c][d]<=e)return; int sum,sum2; cost[c][d]=cost[d][c]=e; for(int s=1;s<=n;s++){ for(int g=1;g<=n;g++){ sum=cost[s][c]+e+cost[d][g]; if(sum<cost[s][g]) cost[s][g]=cost[g][s]=sum; sum=cost[s][d]+e+cost[c][g]; if(sum<cost[s][g]) cost[s][g]=cost[g][s]=sum; } } } void calc(){ for(int i=0;i<=n;i++){ for(int j=0;j<=n;j++){ cost[i][j]=max; if(i==j)cost[i][j]=0; } } int a,b,type,c,d,e; for(int i=0;i<k;i++){ scanf("%d",&type); if(type==0){ scanf("%d %d",&a,&b); printf("%d\n",cost[a][b]==max?-1:cost[a][b]); }else{ scanf("%d %d %d",&c,&d,&e); costUpData(c,d,e); } } } int main(){ while(1){ scanf("%d %d",&n,&k); if(n==0 &&k==0) break; calc(); } } ---- *0527 Setting Go Stones http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0527 碁石の置き換え問題。 解法 連続して同色の碁石が並んでいる部分を塊で扱います。 #include<stdio.h> #include<vector> struct stone{ int count,color; }; void calc(int n){ std::vector<stone> stones; stone s; int color,last; for(int i=1;i<=n;i++){ scanf("%d",&s.color); s.count=1; last=stones.size()-1; if(i%2==1){ if(stones.empty()){ stones.push_back(s); }else if(stones[last].color==s.color){ stones[last].count++; }else{ stones.push_back(s); } }else{ color=stones[last].color; if(color==s.color){ stones[last].count++; }else{ s=stones[last]; stones.pop_back(); if(stones.empty()){ s.color=(s.color+1)%2; s.count++; stones.push_back(s); }else{ stones[last-1].count+=s.count+1; } } } } int ans=0; for(int i=0;i<stones.size();i++){ if(stones[i].color==0)ans+=stones[i].count; } printf("%d\n",ans); } int main(){ int n; while(1){ scanf("%d",&n); if(n==0) break; calc(n); } } ---- *0528 Common Sub-String http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0528 最長一致部分文字列を探す問題。 解法 動的計画法でn^2の計算量で解きます。 #include<stdio.h> #include<string.h> int main(){ char text[4001],text2[4001]; int memo[2][4003],now,old,ans; while(scanf("%s",text)!=EOF){ memset(memo,0,sizeof(memo)); scanf("%s",text2); ans=0; for(int i=0;text2[i]!='\0';i++){ now=i%2; old=(i+1)%2; memset(memo[now],0,4003*4); for(int j=0;text[j]!='\0';j++){ if(text2[i]==text[j]){ memo[now][j+1]=memo[old][j]+1; ans=ans>memo[now][j+1]?ans:memo[now][j+1]; } } } printf("%d\n",ans); } } ---- *0529 Darts http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0529 ダーツを4回投げたときMを超えない可能な最高得点を探す問題。 解法 きれいなコードを用意できませんでした。 1本投げた場合を求め、次に2本投げた場合の全組み合わせを求め、Vectorに保存しておきます。 後は3本の組を1本のセットと2本のセットからバイナリサーチで最高点を求め、4本は2本+2本をバイナリサーチで求めます。 正答者データを見る限りこの問題は下記コードの半分程度のコード量で正答できさらなる高速化も可能なようです。 正答者データ一覧↓ http://judge.u-aizu.ac.jp/onlinejudge/problem_statistics.jsp?id=0529 #include <stdio.h> #include <vector> #include <algorithm> int solve(int n,int m); int main() { int n,m,ans; scanf("%d %d",&n,&m); while(n!=0 && m!=0){ ans=solve(n,m); printf("%d\n",ans); scanf("%d %d",&n,&m); } } int bsearch(std::vector<int> &v,int target){ int l=0,r=v.size(),size=v.size()-1; if(v[0]>target) return 0; if(v[size]<=target) return v[size]; int index; while(1){ index=(l+r)/2; if(v[index]==target) return target; if(index<size && v[index]<target && v[index+1]>target) return v[index]; if(v[index]>target){ r=index; continue; } if(v[index]<target){ l=index+1; continue; } } return -1; } int solve(int n,int m) { std::vector<int> wPoints; std::vector<int>::iterator it; int t; int datas[1001]; int p=0; int ans=0; //一本だけ使う場合 for(int i=0;i<n;i++) { scanf("%d",&t); if(t==m){ return m; }else if(t<m){ datas[p]=t; p++; ans=std::max(ans,t); } } //2本使う場合 for(int i=0;i<p;i++){ for(int j=0;j<=i;j++){ t=datas[i]+datas[j]; if(t==m){ return m; }else if(t<m){ wPoints.push_back(t); ans=std::max(ans,t); } } } std::sort(wPoints.begin(),wPoints.end()); //3本使う場合 for(int i=0;i<p;i++){ t=m-datas[i]; t=datas[i]+bsearch(wPoints,t); ans=std::max(ans,t); if(ans==m) return m; } //4本使う場合 it=wPoints.begin(); while(it!=wPoints.end()) { t=m-(*it); //if(t<(*it)) return ans; t=(*it)+bsearch(wPoints,t); ans=std::max(ans,t); if(ans==m) return m; it++; } return ans; } ---- *0530 Pyon-Pyon River Crossing http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0530 川の上に出た石から石へジャンプして川を渡るとき、最も安全なルートをとった場合の危険度を算出する問題。 解法 一行ずつ動的計画法で計算し一番安全なルート以外枝刈していきます。 2段飛ばしジャンプができる回数の制限があるために3次元必要になる点に注意が必要です。 #include<stdio.h> #include<vector> #include <stdlib.h> int n,m; struct Stone{ int x,d;//x座標と滑りやすさ }; void calc(){ int memo[77][154][11];//動的計画法である石に到達したとき滑りやすさの合計が最小となる経路を残す std::vector<Stone> stones[154]; Stone stone; int k;//滑りやすさ int max=1000000000; for(int z=0;z<=(n+1)/2;z++){ for(int y=0;y<n;y++){ for(int x=0;x<11;x++){ if (y==1 && z==1) memo[1][y][x]=0; else if (y==0 && z==0) memo[0][y][x]=0; else memo[z][y][x]=max; } } } for(int y=0;y<n;y++){ scanf("%d",&k); for(int j=0;j<k;j++){ scanf("%d %d",&stone.x,&stone.d); stones[y].push_back(stone); } } int jump,slip; for(int y=0;y<n-1;y++){ jump=(y+1)/2>m?m:(y+1)/2; for(int z=0;z<=jump;z++){ for(unsigned int x=0;x<stones[y].size();x++){ //一段ジャンプ for(unsigned int x2=0;x2<stones[y+1].size();x2++){ slip=memo[z][y][x]+abs(stones[y][x].x-stones[y+1][x2].x)*(stones[y][x].d+stones[y+1][x2].d); if(memo[z][y+1][x2]>slip){ memo[z][y+1][x2]=slip; } } //2段ジャンプ for(unsigned int x2=0;x2<stones[y+2].size();x2++){ slip=memo[z][y][x]+abs(stones[y][x].x-stones[y+2][x2].x)*(stones[y][x].d+stones[y+2][x2].d); if(memo[z+1][y+2][x2]>slip){ memo[z+1][y+2][x2]=slip; } } } } } int ans=max; for(int z=0;z<=m;z++){ for(int x=0;x<11;x++){ ans=memo[z][n-1][x]<ans?memo[z][n-1][x]:ans; if(z==m) continue; ans=memo[z][n-2][x]<ans?memo[z][n-2][x]:ans; } } printf("%d\n",ans); } int main(){ while(1){ scanf("%d %d",&n,&m); if(n==0 && m==0) break; calc(); } }

表示オプション

横に並べて表示:
変化行の前後のみ表示: