PKU演習問メモ(10/23)
毎日少しずつ解けるよう頑張る。
No. | 問題名 | 問題の種類および解法 | 難易度 |
---|---|---|---|
1179 | Polygon | 動的計画法 | ★★☆☆☆ |
1144 | Network | 関節点(グラフ) | ★★★☆☆ |
1179 Polygon
問題概要
n角形の形をしたグラフが与えられる。
各頂点には数が、辺には'+'または'*'の演算子が書かれている。
このグラフに次のような操作を施す。
- 一つの辺を選び、削除する
- 以下次の操作を頂点が最後の一つになるまで繰り返す
- 一辺およびその両端の2頂点の組を選ぶ
- その組を、(左の数) 演算子 (右の数)の数が書かれた頂点に置き換える
このとき、最後に得られる最大の数および、そのような操作の最初に選ぶ辺として可能なものを全て求めよ。
解法
典型的な動的計画法問題。
辺と頂点を置き換える操作は、a1+a2*a3+a4*a5...のような式に括弧を入れていくことに相当する。
最初の辺の削除は、式の先頭を何処にするか決定することになる。
したがって最初の項をn通り決めて、それぞれに対してdp[i][j]にi番目からj番目の項を使って出来る最大の数を入れて、更新していくことが思いつく……が、入力に負の数がありうるため、最小の数も覚えなければならない。
dp[i][j]に最小の数、DP[i][j]に最大の数を覚えたのが下のコード。
ループではなくメモ化再帰で書いたコードはTLEだった。
ソースコード
const ll INF=1LL<<60; int n,d[50]; char o[50]; ll dp[50][50],DP[50][50]; ll solve() { rep(i,n)rep(j,n)dp[i][j]=INF,DP[i][j]=-INF; rep(i,n)dp[i][i]=DP[i][i]=d[i]; rep(len,n)rep(l,n-len) { int r=l+len; for(int m=l;m<r;m++) { ll a=dp[l][m],b=dp[m+1][r],A=DP[l][m],B=DP[m+1][r]; ll c,d,e,f; if(o[m]=='t')c=a+b,d=A+b,e=a+B,f=A+B; else c=a*b,d=A*b,e=a*B,f=A*B; dp[l][r]=min(dp[l][r],min(min(c,d),min(e,f))); DP[l][r]=max(DP[l][r],max(max(c,d),max(e,f))); } } return DP[0][n-1]; } int main() { scanf("%d",&n); rep(i,n)scanf(" %c %d",o+i,d+i); ll mx=-INF,ans[50]; int na=0; rep(i,n) { rotate(o,o+1,o+n); ll t=solve(); if(t>mx)mx=t,na=0; if(t==mx)ans[na++]=i+1; rotate(d,d+1,d+n); } cout<<mx<<endl; rep(i,na)cout<<ans[i]<<(i==na-1?'\n':' '); return 0; }
1144 Network
問題概要
ソースコード
spaghetti source(関節点(http://www.prefield.com/algorithm/graph/bridge.html))のコードを使用
int main() { int n; while(cin>>n,n) { cin.ignore(); Graph g(n); while(1) { string str; getline(cin,str); stringstream ss(str); int s,t; ss>>s; if(s==0)break; s--; while(ss>>t) { t--; g[s].pb(Edge(s,t,0)); g[t].pb(Edge(t,s,0)); } } vi art; articulationPoint(g,art); sort(all(art)); art.erase(unique(all(art)),art.end()); cout<<art.size()<<endl; } return 0; }