SlideShare uma empresa Scribd logo
1 de 20
+ 
Paiza online hackathon 
Genius troubleshooter Kirishima 
ICPC AOJ Meeting 2014/9/8
+ 
Problem 
 あなた(霧島京子) は20万人月の巨大なプロジェクトを一ヶ月で終 
わらせるために無数の下請け会社から人員をかき集める仕事をす 
ることになりました。 
 プロジェクトを終わらせるのに必要な人員数m 名と、発注可能 
な下請け会社の数n 社、各下請け会社のアサイン可能なエンジニ 
ア人員数q_i 名と、発注に必要な費用r_i 万円が与えられます。 
 各下請け会社の人員は、一部を使うなどは出来ず全員を使わなく 
てはいけません。 
 プロジェクトに必要な人員数m 以上を満たせる組み合わせで、最 
も安くすむ合計金額(単位:万円)を出力してください。 
 各下請け会社の人員数の合計はプロジェクトの規模m 人月以上 
になるものとします。 
https://paiza.jp/poh/kirishima
+ 
SourceCode 
http://ideone.com/Fali3g
+ 
Solution 
最初に思いついたやり方 
 会社の数のビット長の2進数を作成する 
ex>会社が4社なら0000~1111までの2進数を作成する 
 この2進数はどの会社を選択するかの組み合わせを表している 
 この2進数で表される組み合せのうち条件を満たし、かつコス 
トが最小になる組み合わせを全探索する。
+ 
Main 
public class Main { 
public static void main(String[] args){ 
Scanner sc = new Scanner(System.in); 
int m = Integer.parseInt(sc.nextLine()); 
int n = Integer.parseInt(sc.nextLine()); 
Company[] companies = new Company[n]; 
for(int i=0;i<companies.length;i++){ 
String[] strs = sc.nextLine().split(" "); 
companies[i] = new Company(Integer.parseInt(strs[0]), 
Integer.parseInt(strs[1])); 
} 
System.out.println(new Execution(m,companies).getResult()); 
} 
} 
データの入力 
Companyは会社情報を格納するクラ 
スでその会社の人員数と費用を持つ 
計算結果の出力
+ 
Execution 
※コンストラクタ部は省略 
private void calculation(){ 
result = null; 
for(int i=0;i<bg.size();i++){ 
Result tmp = new Result(bg.get(i),companies); 
if(result==null&&m<=tmp.getNum()){ 
result=tmp; 
} 
if(m<=tmp.getNum()&&tmp.getCost()<result.getCost()) { 
result = tmp; 
} 
} 
} 
public Result getResult(){ 
return result; 
} 
} 
与えられた組み合わせのコストを計算するResultク 
ラスを使用して条件を満たし、かつ最小のコストの 
組み合わせを計算する
+ 
Result 
class Result{ 
private String str; 
private Company[] comps; 
private int num; 
private int cost; 
public Result(String str,Company[] comps){ 
this.str=str; 
this.comps=comps; 
analysis(); 
} 
private void analysis(){ 
num=0; 
cost=0; 
for(int i=0;i<str.length();i++){ 
if(str.charAt(i)=='1'){ 
num+=comps[i].num; 
cost+=comps[i].cost; 
} 
} 
} 
//getter,setterを省略 
} 
ビットが立っている時にその 
会社の人数とコストを加える
+ 
Company 
class Company{ 
public int num; 
public int cost; 
public Company(int num,int cost){ 
this.num=num; 
this.cost=cost; 
} 
public String toString(){ 
return num+" "+cost; 
} 
}
+ 
BitGenerator 
class BitGenerator{ 
private int n; 
private String[] dataSet; 
public BitGenerator(int n){ 
this.n=n; 
dataSet = new String[(int)Math.pow(2,n)]; 
calculation(); 
} 
private void calculation(){ 
for(int i=0;i<dataSet.length;i++){ 
dataSet[i] = complementBit(Integer.toBinaryString(i)); 
} 
} 
private void lambdaCalculation(){ 
IntStream.range(0,dataSet.length).parallel().forEach(e -> 
dataSet[e]=complementBit(Integer.toBinaryString(e))); 
} 
指定されたビット長の2進数 
を生成する
+ private String complementBit(String str){ 
for(int i=str.length();i<n;i++){ 
str="0"+str; 
} 
return str; 
} 
public int size(){ 
return dataSet.length; 
} 
public String get(int index){ 
return dataSet[index]; 
} 
public void print(){ 
for(String s:dataSet){ 
System.out.println(s); 
} 
} 
} 
生成された2進数を整形する 
例えば生成ビット長が8bitのとき 
100011という2進数を00100011に
+ 
実行結果 
http://paiza.jp/poh/kirishima/result/0ddbb023f7e881720e4a93d693d19ab6
+ 
考察 
 ランタイムエラーはおそらく組み合わせ爆発が発生して、メモ 
リを食い過ぎていると考えられる。 
 これは2進数のStringデータが組み合わせの数だけメモリ上に 
保持されるからである。 
 組み合わせを試行するタイプの問題を高速に解く方法を考えな 
ければならない
+ 
組み合わせを2進数で表す理由
+ 
 組み合わせの問題を解くにはforループの入れ子構造を可変に 
できるようにしなければならない。 
ex>10個のデータから3個選択する場合 
for(int i=0;i<data.length;i++){ 
for(int j=0;j<data.length;j++){ 
for(int k=0;k<data.length;k++){ 
//………………….// 
} 
} 
} 
3個選択する場合や5個選択する場合が混在 
するときにどうするか
+ 
 あらかじめ2進数の組み合わせデータを作成しておいてその 
データを順次処理するようにすればこの問題を回避できる。 
 組み合わせの数に応じてメモリを大量に消費する欠点がある。 
※実際に組み合わせ爆発が発生するとメモリをオーバーする 
 そもそも組み合わせを総当りしている時点でスマートではない。 
動的計画法
+ 
動的計画法 
 対象となる問題を複数の部分問題に分割し、部分問題の計算結 
果を記録しながら解いていく手法である 
 動的計画法は一度計算した部分の計算を再度行わないことに 
よって計算量を減らすアルゴリズムである 
 動的計画法は以下の様な手法を用いる 
分割統治法:部分問題を解き、その結果を利用して、問題全体を 
解く 
メモ化:部分問題の計算結果を再利用する
+ 
Main 
import java.util.Scanner; 
/** 
* Created by nullzine on 2014/09/03. 
*/ 
public class Main { 
public static void main(String[] args){ 
Scanner sc = new Scanner(System.in); 
int m = Integer.parseInt(sc.nextLine()); 
int n = Integer.parseInt(sc.nextLine()); 
Corp[] corps = new Corp[n]; 
for(int i=0;i<corps.length;i++){ 
String[] strs = sc.nextLine().split(" "); 
corps[i] = new Corp(Integer.parseInt(strs[0]),Integer.parseInt(strs[1])); 
} 
new Execution(corps,m); 
} 
}
+ 
Execution class Execution{ 
private Corp[] corps; 
private int m; 
public Execution(Corp[] corps,int m){ 
this.corps=corps; 
this.m=m; 
calculation(); 
} 
private void calculation(){ 
int totalMan = 0; 
int totalCost = 0; 
for (int i = 0; i < corps.length; i++) { 
totalMan += corps[i].q; 
totalCost += corps[i].r; 
} 
// 削減可能な人数 
int reducingMax = totalMan - m; 
// 削減可能コストbefore 
int rdCostBefore[] = new int[reducingMax + 1]; 
// 削減可能コストcurrent 
int rdCostCorrent[] = new int[reducingMax + 1]; 
for (int i = 0; i < corps.length; i++) { 
int copyCount = Math.min(corps[i].q, reducingMax + 1); 
for (int j = 0; j < copyCount; j++) { 
rdCostCorrent[j] = rdCostBefore[j]; 
} 
for (int j = corps[i].q; j <= reducingMax; j++) { 
rdCostCorrent[j] = Math.max(rdCostBefore[j],rdCostBefore[j - corps[i].q] + corps[i].r); 
} 
rdCostBefore = rdCostCorrent; 
rdCostCorrent = new int[reducingMax + 1]; 
} 
System.out.println(totalCost - rdCostBefore[reducingMax]); 
} 
}
+ 
Corp 
class Corp{ 
public int q; 
public int r; 
public Corp(int q,int r){ 
this.q=q; 
this.r=r; 
} 
}
+ 
 この問題はいわゆる「ナップサック問題」の類題である 
 ナップサック問題では重量制限をクリアするものの中でナップ 
サック内の品物の価値を最大化するが、この問題では所定以上 
の人員をクリアするものの中でコストを最小化する。といった 
違いがある。 
 この問題は「人員を削減できる範囲内で、削減コストの最大値 
を求める問題」と読み替えることができる。

Mais conteúdo relacionado

Mais procurados

動的計画法の並列化
動的計画法の並列化動的計画法の並列化
動的計画法の並列化Proktmr
 
WUPC2nd G問題
WUPC2nd G問題WUPC2nd G問題
WUPC2nd G問題Dai Hamada
 
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401Shota Onishi
 
Ag and ack
Ag and ackAg and ack
Ag and ackJoe_noh
 
ジャパネットQB GPars
ジャパネットQB GParsジャパネットQB GPars
ジャパネットQB GParsTakahiro Sugiura
 
構造化オーバーレイネットワークを用いた条件付きマルチキャストの提案
構造化オーバーレイネットワークを用いた条件付きマルチキャストの提案構造化オーバーレイネットワークを用いた条件付きマルチキャストの提案
構造化オーバーレイネットワークを用いた条件付きマルチキャストの提案Kota Abe
 
すごいHaskell読書会 in 大阪 2週目 #5 第5章:高階関数 (2)
すごいHaskell読書会 in 大阪 2週目 #5 第5章:高階関数 (2)すごいHaskell読書会 in 大阪 2週目 #5 第5章:高階関数 (2)
すごいHaskell読書会 in 大阪 2週目 #5 第5章:高階関数 (2)Yoichi Nakayama
 

Mais procurados (8)

動的計画法の並列化
動的計画法の並列化動的計画法の並列化
動的計画法の並列化
 
Aaなゲームをjsで
AaなゲームをjsでAaなゲームをjsで
Aaなゲームをjsで
 
WUPC2nd G問題
WUPC2nd G問題WUPC2nd G問題
WUPC2nd G問題
 
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
Rubyとクラウドサービスで実現したEC決済共通基盤@Ruby Association ビジネスセミナー201401
 
Ag and ack
Ag and ackAg and ack
Ag and ack
 
ジャパネットQB GPars
ジャパネットQB GParsジャパネットQB GPars
ジャパネットQB GPars
 
構造化オーバーレイネットワークを用いた条件付きマルチキャストの提案
構造化オーバーレイネットワークを用いた条件付きマルチキャストの提案構造化オーバーレイネットワークを用いた条件付きマルチキャストの提案
構造化オーバーレイネットワークを用いた条件付きマルチキャストの提案
 
すごいHaskell読書会 in 大阪 2週目 #5 第5章:高階関数 (2)
すごいHaskell読書会 in 大阪 2週目 #5 第5章:高階関数 (2)すごいHaskell読書会 in 大阪 2週目 #5 第5章:高階関数 (2)
すごいHaskell読書会 in 大阪 2週目 #5 第5章:高階関数 (2)
 

Semelhante a Meeting4

C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competitionyak1ex
 
ぱっと見でわかるC++11
ぱっと見でわかるC++11ぱっと見でわかるC++11
ぱっと見でわかるC++11えぴ 福田
 
最適化計算の概要まとめ
最適化計算の概要まとめ最適化計算の概要まとめ
最適化計算の概要まとめYuichiro MInato
 
Brief introduction of Boost.ICL
Brief introduction of Boost.ICLBrief introduction of Boost.ICL
Brief introduction of Boost.ICLyak1ex
 
2018/06/23 Sony"s deep learning software and the latest information
2018/06/23 Sony"s deep learning software and the latest information2018/06/23 Sony"s deep learning software and the latest information
2018/06/23 Sony"s deep learning software and the latest informationSony Network Communications Inc.
 
復習と型 Jyoken
復習と型 Jyoken復習と型 Jyoken
復習と型 Jyokenreew2n
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門natrium11321
 
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例Yoshifumi Kawai
 
C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competitionyak1ex
 
はじめてのPython
はじめてのPythonはじめてのPython
はじめてのPythonKatsumi Honda
 

Semelhante a Meeting4 (14)

C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competition
 
ぱっと見でわかるC++11
ぱっと見でわかるC++11ぱっと見でわかるC++11
ぱっと見でわかるC++11
 
最適化計算の概要まとめ
最適化計算の概要まとめ最適化計算の概要まとめ
最適化計算の概要まとめ
 
Brief introduction of Boost.ICL
Brief introduction of Boost.ICLBrief introduction of Boost.ICL
Brief introduction of Boost.ICL
 
2018/06/23 Sony"s deep learning software and the latest information
2018/06/23 Sony"s deep learning software and the latest information2018/06/23 Sony"s deep learning software and the latest information
2018/06/23 Sony"s deep learning software and the latest information
 
復習と型 Jyoken
復習と型 Jyoken復習と型 Jyoken
復習と型 Jyoken
 
C# LINQ入門
C# LINQ入門C# LINQ入門
C# LINQ入門
 
Map
MapMap
Map
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門
 
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
 
C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competition
 
はじめてのPython
はじめてのPythonはじめてのPython
はじめてのPython
 
Boost Tour 1.50.0
Boost Tour 1.50.0Boost Tour 1.50.0
Boost Tour 1.50.0
 
C#6.0の新機能紹介
C#6.0の新機能紹介C#6.0の新機能紹介
C#6.0の新機能紹介
 

Mais de nullzine

第二回ミーティングスライド
第二回ミーティングスライド第二回ミーティングスライド
第二回ミーティングスライドnullzine
 

Mais de nullzine (12)

Meeting19
Meeting19Meeting19
Meeting19
 
Meeting18
Meeting18Meeting18
Meeting18
 
Meeting13
Meeting13Meeting13
Meeting13
 
Meeting11
Meeting11Meeting11
Meeting11
 
Meeting10
Meeting10Meeting10
Meeting10
 
Meeting9
Meeting9Meeting9
Meeting9
 
Meeting8
Meeting8Meeting8
Meeting8
 
Meeting7
Meeting7Meeting7
Meeting7
 
Meeting6
Meeting6Meeting6
Meeting6
 
Meeting5
Meeting5Meeting5
Meeting5
 
第二回ミーティングスライド
第二回ミーティングスライド第二回ミーティングスライド
第二回ミーティングスライド
 
Meeting1
Meeting1Meeting1
Meeting1
 

Meeting4

  • 1. + Paiza online hackathon Genius troubleshooter Kirishima ICPC AOJ Meeting 2014/9/8
  • 2. + Problem  あなた(霧島京子) は20万人月の巨大なプロジェクトを一ヶ月で終 わらせるために無数の下請け会社から人員をかき集める仕事をす ることになりました。  プロジェクトを終わらせるのに必要な人員数m 名と、発注可能 な下請け会社の数n 社、各下請け会社のアサイン可能なエンジニ ア人員数q_i 名と、発注に必要な費用r_i 万円が与えられます。  各下請け会社の人員は、一部を使うなどは出来ず全員を使わなく てはいけません。  プロジェクトに必要な人員数m 以上を満たせる組み合わせで、最 も安くすむ合計金額(単位:万円)を出力してください。  各下請け会社の人員数の合計はプロジェクトの規模m 人月以上 になるものとします。 https://paiza.jp/poh/kirishima
  • 4. + Solution 最初に思いついたやり方  会社の数のビット長の2進数を作成する ex>会社が4社なら0000~1111までの2進数を作成する  この2進数はどの会社を選択するかの組み合わせを表している  この2進数で表される組み合せのうち条件を満たし、かつコス トが最小になる組み合わせを全探索する。
  • 5. + Main public class Main { public static void main(String[] args){ Scanner sc = new Scanner(System.in); int m = Integer.parseInt(sc.nextLine()); int n = Integer.parseInt(sc.nextLine()); Company[] companies = new Company[n]; for(int i=0;i<companies.length;i++){ String[] strs = sc.nextLine().split(" "); companies[i] = new Company(Integer.parseInt(strs[0]), Integer.parseInt(strs[1])); } System.out.println(new Execution(m,companies).getResult()); } } データの入力 Companyは会社情報を格納するクラ スでその会社の人員数と費用を持つ 計算結果の出力
  • 6. + Execution ※コンストラクタ部は省略 private void calculation(){ result = null; for(int i=0;i<bg.size();i++){ Result tmp = new Result(bg.get(i),companies); if(result==null&&m<=tmp.getNum()){ result=tmp; } if(m<=tmp.getNum()&&tmp.getCost()<result.getCost()) { result = tmp; } } } public Result getResult(){ return result; } } 与えられた組み合わせのコストを計算するResultク ラスを使用して条件を満たし、かつ最小のコストの 組み合わせを計算する
  • 7. + Result class Result{ private String str; private Company[] comps; private int num; private int cost; public Result(String str,Company[] comps){ this.str=str; this.comps=comps; analysis(); } private void analysis(){ num=0; cost=0; for(int i=0;i<str.length();i++){ if(str.charAt(i)=='1'){ num+=comps[i].num; cost+=comps[i].cost; } } } //getter,setterを省略 } ビットが立っている時にその 会社の人数とコストを加える
  • 8. + Company class Company{ public int num; public int cost; public Company(int num,int cost){ this.num=num; this.cost=cost; } public String toString(){ return num+" "+cost; } }
  • 9. + BitGenerator class BitGenerator{ private int n; private String[] dataSet; public BitGenerator(int n){ this.n=n; dataSet = new String[(int)Math.pow(2,n)]; calculation(); } private void calculation(){ for(int i=0;i<dataSet.length;i++){ dataSet[i] = complementBit(Integer.toBinaryString(i)); } } private void lambdaCalculation(){ IntStream.range(0,dataSet.length).parallel().forEach(e -> dataSet[e]=complementBit(Integer.toBinaryString(e))); } 指定されたビット長の2進数 を生成する
  • 10. + private String complementBit(String str){ for(int i=str.length();i<n;i++){ str="0"+str; } return str; } public int size(){ return dataSet.length; } public String get(int index){ return dataSet[index]; } public void print(){ for(String s:dataSet){ System.out.println(s); } } } 生成された2進数を整形する 例えば生成ビット長が8bitのとき 100011という2進数を00100011に
  • 12. + 考察  ランタイムエラーはおそらく組み合わせ爆発が発生して、メモ リを食い過ぎていると考えられる。  これは2進数のStringデータが組み合わせの数だけメモリ上に 保持されるからである。  組み合わせを試行するタイプの問題を高速に解く方法を考えな ければならない
  • 14. +  組み合わせの問題を解くにはforループの入れ子構造を可変に できるようにしなければならない。 ex>10個のデータから3個選択する場合 for(int i=0;i<data.length;i++){ for(int j=0;j<data.length;j++){ for(int k=0;k<data.length;k++){ //………………….// } } } 3個選択する場合や5個選択する場合が混在 するときにどうするか
  • 15. +  あらかじめ2進数の組み合わせデータを作成しておいてその データを順次処理するようにすればこの問題を回避できる。  組み合わせの数に応じてメモリを大量に消費する欠点がある。 ※実際に組み合わせ爆発が発生するとメモリをオーバーする  そもそも組み合わせを総当りしている時点でスマートではない。 動的計画法
  • 16. + 動的計画法  対象となる問題を複数の部分問題に分割し、部分問題の計算結 果を記録しながら解いていく手法である  動的計画法は一度計算した部分の計算を再度行わないことに よって計算量を減らすアルゴリズムである  動的計画法は以下の様な手法を用いる 分割統治法:部分問題を解き、その結果を利用して、問題全体を 解く メモ化:部分問題の計算結果を再利用する
  • 17. + Main import java.util.Scanner; /** * Created by nullzine on 2014/09/03. */ public class Main { public static void main(String[] args){ Scanner sc = new Scanner(System.in); int m = Integer.parseInt(sc.nextLine()); int n = Integer.parseInt(sc.nextLine()); Corp[] corps = new Corp[n]; for(int i=0;i<corps.length;i++){ String[] strs = sc.nextLine().split(" "); corps[i] = new Corp(Integer.parseInt(strs[0]),Integer.parseInt(strs[1])); } new Execution(corps,m); } }
  • 18. + Execution class Execution{ private Corp[] corps; private int m; public Execution(Corp[] corps,int m){ this.corps=corps; this.m=m; calculation(); } private void calculation(){ int totalMan = 0; int totalCost = 0; for (int i = 0; i < corps.length; i++) { totalMan += corps[i].q; totalCost += corps[i].r; } // 削減可能な人数 int reducingMax = totalMan - m; // 削減可能コストbefore int rdCostBefore[] = new int[reducingMax + 1]; // 削減可能コストcurrent int rdCostCorrent[] = new int[reducingMax + 1]; for (int i = 0; i < corps.length; i++) { int copyCount = Math.min(corps[i].q, reducingMax + 1); for (int j = 0; j < copyCount; j++) { rdCostCorrent[j] = rdCostBefore[j]; } for (int j = corps[i].q; j <= reducingMax; j++) { rdCostCorrent[j] = Math.max(rdCostBefore[j],rdCostBefore[j - corps[i].q] + corps[i].r); } rdCostBefore = rdCostCorrent; rdCostCorrent = new int[reducingMax + 1]; } System.out.println(totalCost - rdCostBefore[reducingMax]); } }
  • 19. + Corp class Corp{ public int q; public int r; public Corp(int q,int r){ this.q=q; this.r=r; } }
  • 20. +  この問題はいわゆる「ナップサック問題」の類題である  ナップサック問題では重量制限をクリアするものの中でナップ サック内の品物の価値を最大化するが、この問題では所定以上 の人員をクリアするものの中でコストを最小化する。といった 違いがある。  この問題は「人員を削減できる範囲内で、削減コストの最大値 を求める問題」と読み替えることができる。