「AOJ Problem Set from DPL 問0~4」の編集履歴(バックアップ)一覧はこちら

AOJ Problem Set from DPL 問0~4」(2014/01/28 (火) 08:39:12) の最新版変更点

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

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

*Combinatorial - Coin Changing Problem http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DPL_1_A 金額を払うのに必要な最小コイン枚数をDPで解く問題。 #include<stdio.h> #include<vector> int main(){ int n,m,memo[50001]={0}; scanf("%d %d",&n,&m); int coin; for(int i=0;i<m;i++){ scanf("%d",&coin); for(int j=0;j+coin<=n;j++){ if(memo[j]==0&&j>0)continue; if(memo[j+coin]==0||memo[j+coin]>memo[j]+1){ memo[j+coin]=memo[j]+1; } } } printf("%d\n",memo[n]); } *Combinatorial - 0-1 Knapsack Problem http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DPL_1_B 重さ付きナップザック問題。 #include<stdio.h> #include<vector> int main(){ int n,W,memo[10001]={0}; scanf("%d %d",&n,&W); int v,w,sum=0,ans=0; while(n--){ scanf("%d %d",&v,&w); if(sum+w>W)sum=W-w; for(int j=sum;j>=0;j--){ if(memo[j+w]<memo[j]+v)memo[j+w]=memo[j]+v; } sum+=w; } for(int i=0;i<=W;i++)if(ans<memo[i])ans=memo[i]; printf("%d\n",ans); } *Combinatorial - Longest Increasing Subsequence http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DPL_1_D 数列の中の最長増加部分列の長さをこたえる問題。 最初に書いたコードが尾所方ので書き直したが2倍程度しか早くならなかった。 ある部分列があってそれが例えば 1,2,5 1,3 1,7 1,11 で次の数が8なら 1,2,5,8以外は考慮する意味がない。 という視点で計算量を切り落としていけばまあ平凡なタイムを出せます。 #include<stdio.h> #include<map> std::map<int,int> memo1; int memo[100001]={0}; int main(){ int n,a; std::map<int,int>::iterator it1,it2; scanf("%d",&n); scanf("%d",&a); memo[0]=-1; memo[1]=a; n--; memo1[-1]=0; memo1[a]=1; while(n--){ scanf("%d",&a); it1=it2=memo1.lower_bound(a); it2--; if(it1==memo1.end()){ memo[(*it2).second+1]=a; memo1[a]=(*it2).second+1; }else{ if((*it2).first<a&&a<(*it1).first){ int num1=(*it1).first; int count1=(*it1).second; memo[count1]=a; memo1[a]=count1; memo1.erase(num1); } } } int ans=1; for(int i=100000;i>=0;i--){ if(memo[i]>0){ ans=i; break; } } printf("%d\n",ans); } *Permutation/Path - Traveling Salesman Problem http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DPL_2_A 巡回セールスマン問題。 解法 この問題とても奥深い研究結果がたくさんあるそうですが私は とりあえずこの問題が解ければいいやというスタンスでコードを書きました。 1点訪れた集合から2点訪れた集合を 2点訪れた集合から3点訪れた集合を、、、 と続けるだけの単純な動的計画法です。 #include<stdio.h> #include<map> struct S{ int nowP,bits; bool operator<(const S& s)const{ if(nowP!=s.nowP)return nowP<s.nowP; return bits<s.bits; } }; const int NIL=-1; int G[15][15]; int main(){ std::map<S,int> now,next; std::map<S,int>::iterator it; int V,E,s,t,d; for(int i=0;i<15;i++){ for(int j=0;j<15;j++){ G[i][j]=-1; } } scanf("%d %d",&V,&E); for(int i=0;i<E;i++){ scanf("%d %d %d",&s,&t,&d); G[s][t]=d; } S s1,s2; s1.nowP=0; s1.bits=0; now[s1]=0; for(int i=0;i<V;i++){ for(it=now.begin();it!=now.end();it++){ s1=(*it).first; for(int j=0;j<V;j++){ if(j==0&&i<V-1)continue; int g=G[s1.nowP][j]; if(g==-1)continue; int addBit=(1<<j); if((s1.bits&addBit)>0)continue; g+=(*it).second; s2.nowP=j; s2.bits=(s1.bits|addBit); if(next.find(s2)==next.end() || next[s2]>g){ next[s2]=g; } } } now.clear(); now.insert(next.begin(),next.end()); next.clear(); } it=now.begin(); int ans=-1; if(it!=now.end())ans=(*it).second; printf("%d\n",ans); }
*Combinatorial - Coin Changing Problem http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DPL_1_A 金額を払うのに必要な最小コイン枚数をDPで解く問題。 #include<stdio.h> #include<vector> int main(){ int n,m,memo[50001]={0}; scanf("%d %d",&n,&m); int coin; for(int i=0;i<m;i++){ scanf("%d",&coin); for(int j=0;j+coin<=n;j++){ if(memo[j]==0&&j>0)continue; if(memo[j+coin]==0||memo[j+coin]>memo[j]+1){ memo[j+coin]=memo[j]+1; } } } printf("%d\n",memo[n]); } *Combinatorial - 0-1 Knapsack Problem http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DPL_1_B 重さ付きナップザック問題。 #include<stdio.h> #include<vector> int main(){ int n,W,memo[10001]={0}; scanf("%d %d",&n,&W); int v,w,sum=0,ans=0; while(n--){ scanf("%d %d",&v,&w); if(sum+w>W)sum=W-w; for(int j=sum;j>=0;j--){ if(memo[j+w]<memo[j]+v)memo[j+w]=memo[j]+v; } sum+=w; } for(int i=0;i<=W;i++)if(ans<memo[i])ans=memo[i]; printf("%d\n",ans); } *Combinatorial - Longest Increasing Subsequence http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DPL_1_D 数列の中の最長増加部分列の長さをこたえる問題。 最初に書いたコードが低速なので書き直したが2倍程度しか早くならなかった。 これくらいならシンプルな最初のコードのほうが良かったかもしれない。 ある部分列があってそれが例えば 1,2,5 1,3 1,7 1,11 で次の数が8なら 1,2,5,8以外は考慮する意味がない。 という視点で計算量を切り落としていけばまあ平凡なタイムを出せます。 #include<stdio.h> #include<map> std::map<int,int> memo1; int memo[100001]={0}; int main(){ int n,a; std::map<int,int>::iterator it1,it2; scanf("%d",&n); scanf("%d",&a); memo[0]=-1; memo[1]=a; n--; memo1[-1]=0; memo1[a]=1; while(n--){ scanf("%d",&a); it1=it2=memo1.lower_bound(a); it2--; if(it1==memo1.end()){ memo[(*it2).second+1]=a; memo1[a]=(*it2).second+1; }else{ if((*it2).first<a&&a<(*it1).first){ int num1=(*it1).first; int count1=(*it1).second; memo[count1]=a; memo1[a]=count1; memo1.erase(num1); } } } int ans=1; for(int i=100000;i>=0;i--){ if(memo[i]>0){ ans=i; break; } } printf("%d\n",ans); } *Permutation/Path - Traveling Salesman Problem http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=DPL_2_A 巡回セールスマン問題。 解法 この問題とても奥深い研究結果がたくさんあるそうですが私は とりあえずこの問題が解ければいいやというスタンスでコードを書きました。 1点訪れた集合から2点訪れた集合を 2点訪れた集合から3点訪れた集合を、、、 と続けるだけの単純な動的計画法です。 #include<stdio.h> #include<map> struct S{ int nowP,bits; bool operator<(const S& s)const{ if(nowP!=s.nowP)return nowP<s.nowP; return bits<s.bits; } }; const int NIL=-1; int G[15][15]; int main(){ std::map<S,int> now,next; std::map<S,int>::iterator it; int V,E,s,t,d; for(int i=0;i<15;i++){ for(int j=0;j<15;j++){ G[i][j]=-1; } } scanf("%d %d",&V,&E); for(int i=0;i<E;i++){ scanf("%d %d %d",&s,&t,&d); G[s][t]=d; } S s1,s2; s1.nowP=0; s1.bits=0; now[s1]=0; for(int i=0;i<V;i++){ for(it=now.begin();it!=now.end();it++){ s1=(*it).first; for(int j=0;j<V;j++){ if(j==0&&i<V-1)continue; int g=G[s1.nowP][j]; if(g==-1)continue; int addBit=(1<<j); if((s1.bits&addBit)>0)continue; g+=(*it).second; s2.nowP=j; s2.bits=(s1.bits|addBit); if(next.find(s2)==next.end() || next[s2]>g){ next[s2]=g; } } } now.clear(); now.insert(next.begin(),next.end()); next.clear(); } it=now.begin(); int ans=-1; if(it!=now.end())ans=(*it).second; printf("%d\n",ans); }

表示オプション

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