square869120Contest #1(A〜D)
現在FとH以外は解いたけど、1つの記事にするとえげつない長さになるので2〜3回に分割します。
今回はA〜D問題。比較的簡単で、自分もコンテスト中(夕飯食べたり風呂入ったりで参加したのは実質1時間)でDまでは解きました。
A - E869120列車 (E869120 Trains)
問題概要
5:00から23:00までの間に電車がN本等間隔で走ったそうです。最初の1本が5:00丁度、最後の1本が23:00丁度に通った時、何分間隔で電車が通ったか求めよ。
A: E869120列車 (E869120 Trains) - square869120Contest #1 | AtCoder
解法
5:00〜23:00は18時間=18×60分。その間にN本走ったので、18×60/(N-1)を出力するだけです。
#include <bits/stdc++.h> using namespace std; int main(){ int n; cin>>n; printf("%.15lf\n",18*60.0/(n-1)); return 0; }
B - ケーキ・カッティング (Cake Cutting)
問題概要
長方形の座標の格子点上にイチゴがいくつかあります。(0,0)と座標の端の格子点のを結ぶ直線で長方形を分割し、イチゴを等分してください。
B: ケーキ・カッティング (Cake Cutting) - square869120Contest #1 | AtCoder
解法
H,W,N≦10と数がかなり小さいので普通に全探索。(0,0)以外の交点を2重ループで回しながら(最初にその格子点が端か判定)、各イチゴが直線のどちら側にあるか判定するのですが、自分は座標を用いました。
(0,0)、(a,b)を通る直線はay=bxです。イチゴの座標を(i,j)とした時、
- イチゴが直線より上にあるならay>bx
- イチゴが直線上にあるならay=bx
- イチゴが直線より下にあるならay<bx
2.は問題文より該当するイチゴがあった時点で不適。1と3のイチゴの数が一致すればその格子点は条件を満たします。
#include <bits/stdc++.h> using namespace std; int main(){ int h,w,n; cin>>h>>w>>n; vector<pair<int,int>> a(n); for(int i=0;i<n;i++) cin>>a[i].first>>a[i].second; bool unuse=true; for(int i=1;i<=w;i++){ for(int j=1;j<=h;j++){ if(i!=w&&j!=h) continue; //(i,j)でカット。iy=jx。 int left=0,right=0; for(int k=0;k<n;k++){ int x=j*a[k].first,y=i*a[k].second; if(x==y){ left=0; right=-1; break; } else if(x>y) left++; else right++; } if(left==right){ unuse=false; cout<<"("<<i<<","<<j<<")"<<endl; } } } if(unuse) cout<<-1<<endl; return 0; }
C - お金の街 (The Money Town)
問題概要
各街にお金がおいてあり、それを回収しながら散歩する。始点は自由で同じ街を通らずに散歩するとき、最大いくらお金を集められるか。
C: お金の街 (The Money Town) - square869120Contest #1 | AtCoder
解法
散策可能な通り数が200万通り以下なので全探索して1億回。ギリギリ間に合いました。
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll n,k; vector<ll> d; vector<int> e[55]; bool visited[55]; ll dfs(int cur){ visited[cur]=true; ll ret=0; for(int i=0;i<(int)e[cur].size();i++){ if(!visited[e[cur][i]]) ret=max(ret,dfs(e[cur][i])); } visited[cur]=false; return ret+d[cur]; } int main(){ cin>>n>>k; for(int i=0;i<n;i++){ ll id; cin>>id; d.push_back(id); } for(int i=0;i<k;i++){ int x,y; cin>>x>>y; x--; y--; e[x].push_back(y); e[y].push_back(x); } ll ans=0; for(int i=0;i<n;i++){//start point ans=max(ans,dfs(i)); } cout<<ans<<endl; return 0; }
D - square1001の通学経路 (square1001's School Road)
問題概要
座標上で(1,1)から(W,H)まで格子点を最短距離で進むが、(i,j)(i+1,j)(i+1,j+1)(i,j+1)のいづれかを通らなければならない。
この条件を満たす移動方法は何通りあるか。
D: square1001の通学経路 (square1001's School Road) - square869120Contest #1 | AtCoder
解法
最短距離で進む=戻れない、です。ここで、上に挙げた頂点が通れなくなるのは(i-1,j+2)含む左上、もしくは(i+2,j-1)を含む右下を通ってしまった時です。これらを通ってはいけないので予め-1とでも値を初期化しておけばあとは見慣れたDPでOKです。
(今気が付きましたが、この処理は累積和使うと更に早くなるんですかね…)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1000000007; ll dp[1200][1200]; int main(){ int h,w,k; cin>>h>>w>>k; memset(dp,0,sizeof(dp)); for(int i=0;i<k;i++){ int x,y; cin>>x>>y; //(x+2,y-1)より右下と(x-1,y+2)より左上を0に。 for(int a=x+2;a<=w;a++){ for(int b=y-1;b>=0;b--) dp[b][a]=-1; } for(int b=y+2;b<=h;b++){ for(int a=x-1;a>=0;a--) dp[b][a]=-1; } } dp[1][1]=1; for(int i=1;i<=h;i++){ for(int j=1;j<=w;j++){ if(dp[i][j]==-1) continue; dp[i][j]%=mod; if(i+1<=h&&dp[i+1][j]!=-1){ dp[i+1][j]+=dp[i][j]; } if(j+1<=w&&dp[i][j+1]!=-1){ dp[i][j+1]+=dp[i][j]; } } } cout<<dp[h][w]<<endl; return 0; }
続きは週末にでも。(F問題だけがサッパリ分からない)