AOJの問題で質問あります

このQ&Aのポイント
  • AOJの問題で考えても全然わからない問題があり、他の人の解答例を参考にしようと思い、見てみたのですが、一つ分からないところがあり、質問させていただきます。
  • 問題URL:[http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0089](http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0089) 自分が参考にしている解答例のURL:[http://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=570672](http://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=570672)
  • 質問内容:「solve」関数の役割や値の返り値が分からないです。解答例では計算結果が`data[cnt-1][0]`に格納されていますが、詳細が分かりません。また、`solve`関数内の処理の詳細や端のみを足している`data`の役割も教えていただきたいです。
回答を見る
  • ベストアンサー

AOJの問題で、質問があります。

AOJの問題で考えても全然わからない問題があり、他の人の解答例を参考にしようと思い、見てみたのですが、一つ分からないところがあり、質問させていただきます。 問題です。 http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0089 自分が参考にしている解答例です。 http://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=570672 以下は分かっていないところを書かせていただきます。 なお、コードは解答例と同じです。 include <stdio.h> #include <string.h> int max(int a,int b){ return (a > b) ? a : b; } /*上の数、i-1行目の数から、i行目の大きい数をとっていく関数だと思いましたが、じっくり考えてみると、何パターンかのdataの足し算をしていることに気づきました。まずは左端のみ足す場(data[i+1][0] += data[i][0];)、次に右端だけを足す場合( data[i+1][i+1] += data[i][i];) 、そして for(j = 1;j <= i;j++){ data[i+1][j] += max(data[i][j-1],data[i][j]); } という、端以外の数から足すものです。ここで分からないことは ・このdata[i+1][j] += max(data[i][j-1],data[i][j]);という解答はi+1行目から、i行目の大きな数を選んでいますが、i行目からi+1行目の隣の大きいほうの数を足すようにしないと、ただしい経路は求まらないと思うのですが、どうでしょうか?また、端のみを足しているdataは、どういう役割をしているのでしょうか? 試しに端だけを足す式を削除したら、間違いとなってしまいます。また、この関数はreturn などがないですが、どの値が残るのでしょうか?main関数内では、どのような役割をするのでしょうか?*/ void solve(int data[100][50],int cnt){ int i,j; for(i = 0;i < cnt/2;i++){ data[i+1][0] += data[i][0]; for(j = 1;j <= i;j++){ data[i+1][j] += max(data[i][j-1],data[i][j]); } data[i+1][i+1] += data[i][i]; } for(i = cnt/2+1;i < cnt;i++){ for(j = 0;j < cnt-i;j++){ data[i][j] += max(data[i-1][j],data[i-1][j+1]); } } } int main(){ int h,i,j,cnt,len,num; int data[100][50]; char str[1024]; memset(data,0,sizeof(data)); i = 0; cnt = 0; while(scanf("%s",str) != EOF){ h = 0; j = 0; len = strlen(str); while(h <= len){ if(h < len && str[h] != ','){ num = 0; while(h < len && str[h] != ','){ num *= 10; num += str[h]-'0'; h++; } data[i][j] = num; j++; } h++; } i++; cnt++; } solve(data,cnt);//ここでは、計算した結果、どのような値が帰ってくるのでしょうか? printf("%d\n",data[cnt-1][0]); return 0; } 長文失礼しました。 よろしくお願いします。

noname#230052
noname#230052

質問者が選んだベストアンサー

  • ベストアンサー
  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.1

コメントがまったく無いので、わかりずらいプログラムになってしまっています。 「何をしているか?」を考えてコメントを付けてみてはどうでしょうか。 > //ここでは、計算した結果、どのような値が帰ってくるのでしょうか? voidですから、戻り値はありませんが、第一引数で指定した配列が変更されます。 どうしてそうなるか理解できない場合は、参考書等でポインタの勉強してください > printf("%d\n",data[cnt-1][0]); 結果らしきものを出力している箇所はここだけです。 ということは ・data[cnt-1][0] が結果ではないだろうか? と予想できます。すると ・配列 data には、もともと入力したデータが入っていたはず ・入力から出力までの間に solve関数を実行している ということから、solve関数で配列dataが書き変わっているのでは?と予想できます。 ○solve関数の動作 実際に、コンピュータになったつもりで、動作を追い掛けてはですか? ・i=0 のとき、 data[0] : a0 data[1] : b0 b1 data[2] : c0 c1 c2 data[i+1][0] += data[i][0]; → data[1][0] += data[0][0] ; → data[1][0] +=a0 data[i+1][i+1] += data[i][i]; → data[1][1] += data[0][0] ; → data[1][1] +=a0 j=1 は j<=iではないので、forループは実行されず 結果 data[0] : a0 data[1] : b0+a0 b1+a0 data[2] : c0 c1 c2 ・i=1のとき data[i+1][0] += data[i][0]; → data[2][0] += data[1][0] ; → data[2][0] +=b0+a0 data[i+1][i+1] += data[i][i]; → data[2][2] += data[1][1] ; → data[2][2] +=b1+a0 j=1 data[i+1][j] += max(data[i][j-1],data[i][j]); →data[2][1] += max(data[1][0],data[1][1]); →data[2][1] += max(b0+a0,b1+a0); 結果 data[0] : a0 data[1] : b0+a0 b1+a0 data[2] : c0+b0+a0 c1+max(b0+a0,b1+a0) c2+b1+a0 ここまで追い掛けたら、solveがどんな方法で結果をだそうとしているのか、わかるのではないでしょうか? 幅が広がっていく部分では、候補は1つしかありません。 あえてやるなら次のようになります。 for(j = 0;j <= i+1;j++) として ・data[i+1][0] += max(data[i][-1],data[i][0]); // ただし、範囲外となる data[i][-1]は0とする ・data[i+1][i+1] += max(data[i][i],data[i][i+1]); // ただし、範囲外となる data[i][i+1]は0とする data[i][i+1]の方は、サイズを大きく取って、予め0にしておく、という方法で対応できます。 しかし、data[i][-1]は先に添字をチェックする必要があります。 それなら、候補が1つしか無い場合は、そのまま計算した方がいいのではないでしょうか。

noname#230052
質問者

お礼

なるほど! 具体的に書いてみると、この関数の動きが分かりますね! やっと理解できました。ありがとうございます。 あと、ご指摘の通り、回答者様に理解されやすいような説明、書き方をしていくことを念頭に置き、今後はやっていこうと思います。

関連するQ&A

  • 問題です。

    #include <stdio.h> short X1(char *,char *,char *); void X2(char *,short,short); void X3(char *,short,char *); void main(void) { char Str[][30]={"CDECDEGEDCDEC","CDE","AB"}; short Ret; Ret=X1(Str[0],Str[1],Str[2]); printf("Str[0] : %s\n", Str[0]); printf("Str[1] : %s\n", Str[1]); printf("Str[2] : %s\n", Str[2]); printf("Ret : %d\n", Ret); } short X1(char *pStr1,char *pStr2,char *pStr3) { short Len1,Len2,Len3; short Cnt1,Cnt2; short Ret = 0; for (Len1= 0;pStr1[Len1]!=0;Len1++); for (Len2= 0;pStr2[Len2]!=0;Len2++); for (Len3= 0;pStr3[Len3]!=0;Len3++); if (Len1<Len2 || Len2==0) return Ret; for (Cnt1=0; Cnt1<=Len1-Len2; Cnt1++){ for (Cnt2=0; Cnt2<Len2; Cnt2++){ if (pStr1[Cnt1+Cnt2]!=pStr2[Cnt2]) break; } if (Cnt2==Len2){ X2(pStr1,Cnt1+1,Len2); X3(pStr1,Cnt1+1,pStr3); Len1 += Len3-Len2; Cnt1 += Len3-1; Ret++; } } return Ret; } void X2(char *pStr, short Pnt, short Num) { short Cnt, Len; for (Len = 0; pStr[Len]!=0; Len++); for (Cnt = Pnt+Num-1; Cnt<=Len; Cnt++){ pStr[Cnt-Num] = pStr[Cnt]; } } void X3(char *pDst, short Pnt; char *pSrc) { short SrcLen, DstLen; short Cnt; for (SrcLen=0; pSrc[SrcLen]!=0; SrcLen++); for (DstLen=0; pDst[DstLen]!=0; DstLen++); for (Cnt=DstLen-1; Cnt>=Pnt-1; Cnt--){ pDst[Cnt+SrcLen]=pDst[Cnt]; } for (Cnt=0; Cnt<SrcLen; Cnt++){ pDst[Pnt+Cnt-1]=pSrc[Cnt]; } pDst[DstLen+SrcLen]=0; } というようなプログラムの出力結果はどうなるか?という問題ですが、Str[0]:ABABGEDABC, Str[1]:CDE, Str[2]:AB, Ret:3となるんですが、どうしてそうなるかが、全然分かりません。どなたか説明してもらえないでしょうか?お願いします。

  • 加算、減算、乗算、除算について

    // 加算、減算、乗算、除算について // 記号と数値にそれぞれ別の配列に分けました。 // そこから、どうすれば計算ができるのか // 悩んでいます。よろしくお願いします。 #include<iostream> using namespace std; char **tokei(char *str1,char *str2,int *count,char *kigouX); int main() { int count; int *num; char **www; char kigo[12]; char str1[30],str2[]="+-*/"; strcpy(str1,"123+45-6*789/"); www=tokei(str1,str2,&count,kigo); num=new int[count]; for(int n=0;n<count;n++) { num[n]=atoi(www[n]); } // 数値に変換num[]、記号を順番に抽出kigo[] -->OK // 記号に沿って、数値を演算すればよい。 // ここがやり方(理屈)がわからない。 getchar();{}return 0; } char **tokei(char *str1,char *str2,int *count,char *kigouX) { int cnt=0; int m=0; for(int a=0;*(str1+a)!='\0';a++){} for(int b=0;*(str2+b)!='\0';b++){} for(int i=0;i<a;i++) { for(int j=0;j<b;j++) { if(*(str1+i)==*(str2+j)) { *(kigouX+m)=*(str1+i);m++; *(str1+i)='\0'; cnt++; } } } *(kigouX+m)='\0'; char **c; c=new char*[cnt]; *count=cnt; for(int m=0;m<cnt;m++) { c[m]=str1; while(*str1!='\0'){str1++;}str1++; } return c; }

  • テキストファイルから読み込ませたい

    このプログラムをテキストファイルから読み込ませたいのですが、 どう改良したらいいかわかりません。どなたかプログラムの追加を教えていたたけないでしょうか。 #include <stdio.h> int main() { int i,key,len,num ; char str[256],*ptr[128] ; num = 0 ; len = 0 ; ptr[0] = str ; do { key = getchar(); str[len] = (char)key ; if ( key == ' ' || key == '.' || key == ',' || key == '!' || key == '?' || key == '"' || key == 0x0a || key == 0x0d ){ str[len] = '\0' ; if ( str+len-ptr[num] ){ num ++ ; } ptr[num] = str+len+1 ; if( key=='.' || key== '!' || key=='?' || key=='"'){ str[++len]=(char)key; str[++len]='\0'; ptr[++num]=&str[len+1]; } } len ++ ; } while ( key != 0x0a && key != 0x0d && len < 255 ); str[255] = '\0' ; for (i=0 ;i<num ;i++){ printf("%d. %s\n",i+1,ptr[i]); } return i ; }

  • C言語のファイル操作についての質問です

    #include <stdio.h> #include<process.h> int main(void) { FILE *fp; int a[200], i, j, cnt, max, max_i; fp = fopen("data.txt", "r"); if (fp == NULL) { printf("file cannot open.\n"); exit(1); } for(i = 0; i < 200 && fscanf(fp, "%d", &a[i]) == 1; ++ i) ; fclose(fp); for(max = max_i = j = 0; j < i; ++ j){ int k; for(cnt = 0, k = j + 1; k < i; ++ k) cnt += (a[j] == a[k]); if(cnt > max) max = cnt; max_i = j; } printf("%d\n", a[max_i]); return 0; } これは「data.txt」というファイルから最頻値を探し出し、その値を表示するプログラムです。 しかし、このプログラムだと最頻値が1つしか表示できないので、 最頻値が複数ある場合でも、すべての最頻値の値を表示させるようなプログラムに書き換えてほしいです。 よろしくお願いします。 例)data.txt 30000 100 150 30000 30000 100 4320 100 出力↓ 30000 100

  • このプログラムの問題点を教えてください。

    //数独 #include <iostream.h> const int num_Max = 100; //一辺のマス最高値 void input_data( int a, int [num_Max][num_Max] ); //入力関数 void yoko_data( int a, const int [100][100], int & ); //判定 void tate_data( int a, const int [100][100], int & ); void block_data( int a, int b, const int [100][100], int & ); main() { int num; //一辺のマスの数 int m; //一ブロックの一辺にあるマスの数 int okey; //numが正常か判別 int dx, dy, dz; int masu[num_Max][num_Max]; //全部のマス cout << "\n埋められた数独が正しいかどうか判断するプログラムです。\n"; while(1){ cout << "横何マスありますか? (4-100)>>" ; cin >> num ; if( num > num_Max || num < 4){ cout <<"範囲外です。再入力して下さい。" ; }else{ m = sqrt( num ); okey = num - m * m; if(okey != 0) cout << "その数字は数独として成り立ちません。再入力して下さい。"; } } //マスの入力 input_data( num, masu ); //横判定 yoko_data( num, masu, dx ); if(dx = 0){ //縦判定 tate_data( num, masu, dy ); if(dy = 0){ //マス判定 block_data( num, m, masu, dz ); } } if(dx == dy == dz == 0) cout <<"\n大正解♪ おめでっとー。\n"; return 0; } //入力 void input_data( int kazu, const int matrix[num_Max][num_Max]) { cout <<"\nマスの数字の入力を左上から順に、右へと行って下さい。"; for(int i = 0; i < kazu; i++){ for(int j = 0; j < kazu; j++){ cout << i+1 << "行目の" << j+1 << "列目 >>"; cin >> matrix[i][j] ; } } } //横列(行)を順に判定 void yoko_hantei( int kazu, int matrix[num_Max][num_Max]) { int kaburi; //かぶっているか判定する用 int vx = 0; //かぶっていたと判定されたかどうかを見る用 for(int i = 0; i < kazu; i++){ //横一列取り出しました。 for(int j = 0; j < kazu; j++){ for( int k = 1; j+k < kazu; k++){ kaburi = matrix[i][j] - matrix[i][j+k]; if(kaburi == 0){ //かぶってたらループから抜け出す vx++; break; } } } } if(vx > 0) cout << "\n横列(行)検索時に不適切な部分を発見しました。\n"; return vx; } //縦列(列)を順に判定 void tate_hantei( int kazu, int matrix[num_Max][num_Max]) { int kaburi; //かぶっているか判定する用 int vy = 0; //かぶっていたと判定されたかどうかを見る用 for(int j = 0; j < kazu; j++){ //縦一列取り出しました。 for(int i = 0; i < kazu; i++){ for( int k = 1; k < kazu-1; k++){ kaburi = matrix[i][j] - matrix[i+k][j]; if(kaburi == 0){ //かぶってたらループから抜け出す vy++; break; } } } } if(vy > 0) cout << "\n縦列(列)検索時に不適切な部分を発見しました。\n"; return vy; } //1ブロックごとに判定 void block_data( int kazu, int ruto, int matrix[num_Max][num_Max]) { int hantei[num_Max][num_Max] ; //判定する部分を切り出す用 int kaburi; int vz; //まずブロックごとに切り出してみる for(int i = 0; i < ruto-1; i++){ for(int j = 0; j < ruto-1; j++){ int h = 0; //何ブロック目か int g = 0; //そのブロックの何個目か (h-1)++; for(int l = 0; l < ruto-1; l++){ for(int k = 0; k < ruto-1; k++){ hantei[h][g++] = matrix[i * m + k][j * m + l]; } } } } //かぶっているか判定する for(int x = 0; x < kazu; x++){ for(int y = 0; y < kazu; y++){ for( int z = 1; z < kazu-1; z++){ kaburi = hantei[x][y] - matrix[x+z][y]; if(kaburi == 0){ //かぶってたらループから抜け出す vz++; break; } } } } return vz; } C++で作成したものです。 コンパイルエラーが出てしまうのですが、原因を教えていただけませんか?

  • このプログラムを改良したい

    この単語分割プログラムを区切り文字である( . ? ! " )等も一つの単語として表示させたいのですが、どう改良したらいいかわかりません。 どなたかプログラムの追加削除を教えていたたけないでしょうか。 #include <stdio.h> int main() { int i,key,len,num ; char str[256],*ptr[128] ; num = 0 ; len = 0 ; ptr[0] = str ; do { key = getchar(); str[len] = (char)key ; if ( key == ' ' || key == '.' || key == ',' || key == '!' || key == '?' || key == '"' || key == 0x0a || key == 0x0d ){ str[len] = '\0' ; if ( str+len-ptr[num] ){ num ++ ; } ptr[num] = str+len+1 ; } len ++ ; } while ( key != 0x0a && key != 0x0d && len < 255 ); str[255] = '\0' ; for (i=0 ;i<num ;i++){ printf("%d. %s\n",i+1,ptr[i]); } return i ; } *教えてgoo 質 問 No.919881 arukamunさんのプログラムを引用しました。

  • 分割した単語の頻出頻度を表示させたい

    英文テキストファイルを読み込み、分割した単語の頻出頻度を表示するプログラムを作成しています。 現時点では、分割した単語の表示しかできていません。 どなたか良きアドバイスをお願いします。 #include <stdio.h> int main() { int i,key,len,num ; int sp=0; char str[100000],*ptr[100000] ; FILE *fp; if ((fp=fopen("test.txt","r"))==NULL) { return -1; } num = 0 ; len = 0 ; ptr[0] = str ; do { key = fgetc(fp); str[len] = (char)key ; if ( (key==' ' && sp==0) || key == '.' || key == ',' || key == '!' || key == '?' || key == '"' || key == 0x0a || key == 0x0d ){ str[len] = '\0' ; if ( str+len-ptr[num] ){ num ++ ; } ptr[num] = str+len+1 ; if( key==',' || key=='.' || key== '!' || key=='?' || key=='"'){ str[++len]=(char)key; str[++len]='\0'; ptr[++num]=&str[len+1]; } } sp= (key== ' ')?1:0; len ++ ; } while ( key != EOF && len < 255 ); str[255] = '\0' ; for (i=0 ;i<num ;i++){ printf("%s\n",ptr[i]); } fclose(fp); return i; }

  • 改善すべき点を教えてください。

    趣味で最近Cを始めたのですが、ある本で「0を除く2種類の数字のみで構成された平方数」は、81619の二乗(6661661161)より大きいものはないらしい。 と書かれていたので、確かめたくなり色々調べながらプログラミングしてみました。 なんとか動くようになったのですが、いまいちスピードが遅いように感じるのです。 そこで、こうすればスピードを速くできる、ここが不自然な書き方になっているなど、どこか改善すべき点があれば教えて欲しく質問しました。 あと、char型からint型に数字を変換する時に恐らく不自然な方法でやっているので、正規の方法がありましたらそれも教えていただけると助かります。 よろしくおねがいします。 ここから――――――――――――――――――――――― #include <stdio.h> #include <string.h> int main(void){   double i;    //平方根   int m;    //整数(for用)   double x;  //平方数   char str[100];  //文字列(途中作業用)   int mono[100];  //1桁の数字(途中作業用)   int len;  //桁数   int num;  //二つ目の数の置場      for( i=1; i<=10000000000; i++){     x=i*i;              //二乗     sprintf(str,"%.0f",x);      //文字列に平方数を変換     len=strlen(str);         //桁数を求める          if(len<=1)       goto PRINT;          //平方数が一桁ならPRINTへ          for( m=0; m<len; m++){       mono[m]=str[m]-48;      //一桁ずつint型に収納+実際の数に調整。     }          m=(int)i;     if( m%2000000==0)       printf("i=%32.0f:ok - ",i);  //進行状況          for( m=0; m<len; m++)       if( mono[m]==0)         goto OUT;         //0が含まれていたらOUTへ     for( m=1; m<len; m++){       if( mono[m]==mono[0])         goto WHENSAME;      //mono[0]と同じ数字だったらWHENSAMEへ       num=m;            //違う数字の一回目の登場場所をnumに代入       break;            // →脱出       WHENSAME:           //ラベル WHENSAME     }     for( m=num+1; m<len; m++){       if( mono[m]==mono[0]);       else if( mono[m]==mono[num]);       else         goto OUT;       }     PRINT:              //ラベル PRINT     printf("%5.0f => %26.0f\n", i, x);     OUT:               //ラベル OUT   }   return 0; }

  • 最大値と最小値を表示したいのですが・・・

    double numに入っている数字から最大値と最小値を求めたいのですが、このままだと両方とも1.000になってしまうんです・・・ どうやったらちゃんと最大値と最小値が表示されるのでしょうか?? 初心者なものでスイマセンが教えてください!! #include<stdio.h> int main(void) { int i,j; double num[]={4.5,3.1,7.0,9.2,1.0,5.7,9.3,2.3,0.3,1.0}; double max,min; for(i=0; i<10; i++) { for(j=0; j<10; j++) { if(num[i]>num[j]) max=num[i]; } } for(i=0; i<10; i++) { for(j=0; j<10; j++) { if(num[i]<num[j]) min=num[i]; } } printf("最大値は%fです。",max); printf("最小値は%fです。",min); return 0; }

  • 最大値を求める

    3つの整数を入力して、最大値を求めるプログラムを作りたいのですが、整数を入力するところまでは うまくいくのですが、結果が、255、と出てしまいます。どこがおかしいのかが解かりません。 どなたか教えていただけませんか? 宜しくお願いします。 #include <stdio.h> int main(void) { int num[3]; int max, i, j; puts("三つの整数を入力してください"); for(i = 0; i < 3; ++i) { printf("整数%d:", (i + 1)); scanf("%d", &num[i]); } max = num[0]; for(j = 0; j < 3; ++j) { if(max < num[j]) { max = num[j]; } } printf("最大値は%dです。\n", max); return 0; }

専門家に質問してみよう