PKU演習問メモ(7/18)
No. | 問題名 | 問題の種類および解法 | 難易度 |
---|---|---|---|
2250 | Compromise | 最長共通部分文字列(動的計画法) | ★★☆☆☆ |
2137 | Cowties | 動的計画法 | ★★☆☆☆ |
2250 Compromise
問題概要
二つの文章が与えられる。これらの文章に共通する単語の並び(連続しなくともよい)のうち、語数の大きいものを出力せよ。
答えが複数ある場合どれを出力してもよい。
解法
最長共通部分文字列の問題。
動的計画法を用いる典型問題で、deq notes(右リンク集参照)の解説なんかが解りやすい気がする。
列A,列Bをi個、j個使って得られる最長共通部分列の長さをdp[i][j]とおく。
すると、
dp[i+1][j+1]=max{dp[i][j]+(A[i]==B[i]), dp[i+1][j], dp[i][j+1]}
の漸化式が成り立つ。よってこの式にしたがって動的計画法を書けばよい。
答えの文字列はこの表を逆に辿ることで、復元できる。
ソースコード
int n,m; int dp[101][101]; int main() { string str; while(cin>>str) { vector<string> w1,w2; w1.pb(str); while(cin>>str,str!="#")w1.pb(str); while(cin>>str,str!="#")w2.pb(str); n=w1.size(); m=w2.size(); rep(i,n+1)rep(j,m+1)dp[i][j]=0; rep(i,n)rep(j,m) { dp[i+1][j+1]=dp[i][j]+(w1[i]==w2[j]); if(dp[i+1][j+1]<dp[i][j+1])dp[i+1][j+1]=dp[i][j+1]; if(dp[i+1][j+1]<dp[i+1][j])dp[i+1][j+1]=dp[i+1][j]; } vector<string> ans; for(int i=n-1,j=m-1;i>=0&&j>=0;) { if(dp[i+1][j+1]==dp[i][j]+(w1[i]==w2[j])) { if(w1[i]==w2[j])ans.pb(w1[i]); i--; j--; } else if(dp[i+1][j+1]==dp[i][j+1])i--; else j--; } reverse(all(ans)); rep(i,ans.size())cout<<ans[i]<<(i==ans.size()-1?"\n":" "); } return 0; }
2137 Cowties
問題概要
n匹の牛がいる。n匹の牛はそれぞれs[i]種類の候補地のどこかにいなければならない。
牛ごとに各候補地の座標が与えられたとき、
0番目の牛-1番目の牛-…-n-1番目の牛-0番目の牛と、輪を作って牛を紐で結ぶために必要な最小の紐の長さを求めよ。
n≦100,s[i]≦40とする。
解法
0番目の牛をどこに置くかが決まっていれば、以下のような動的計画法によりO(ns^2)で解が求められる。なので、最初の牛の置き方をs[0]通り試せばよい。
動的計画法:
dp[i][j]に牛iがj番目の候補地にいるときの、i番目までの牛を結ぶのに必要な紐の長さとすれば、
dp[i+1][j]=min{dp[i][k]+(牛iの候補地kと、牛i+1のjの候補地の距離)}
の漸化式が成り立つのでコードにすればよい。
小数点以下は単に切り捨てれば良いようである(それで数回はまった)
ソースコード
int n,k[100]; vi X[100],Y[100]; double dp[100][40]; double D(int a,int b,int c,int d) { return hypot(X[a][b]-X[c][d],Y[a][b]-Y[c][d]); } double solve(int a) { rep(i,n)rep(j,40)dp[i][j]=INF; dp[0][a]=0; for(int i=1;i<n;i++) { rep(j,k[i-1])rep(l,k[i]) dp[i][l]=min(dp[i][l],dp[i-1][j]+D(i-1,j,i,l)); } double ret=INF; rep(i,k[n-1])ret=min(ret,dp[n-1][i]+D(0,a,n-1,i)); return ret; } int main() { scanf("%d",&n); rep(i,n) { X[i].clear(); Y[i].clear(); scanf("%d",k+i); int a,b; rep(j,k[i])scanf("%d%d",&a,&b),X[i].pb(a),Y[i].pb(b); } double ans=INF; rep(i,k[0])ans=min(ans,solve(i)); printf("%d\n",int(ans*100)); return 0; }