【CodeForces】704 B. Ant Man
【題目】B. Ant Man
【題意】給定n個人的xi,ai,bi,ci,di,起點為s,終點為e,移動:
In simpler words, jumping from i-th chair to j-th chair takes exactly:
- |xi?-?xj|?+?ci?+?bj seconds if j?<?i.
- |xi?-?xj|?+?di?+?aj seconds otherwise (j?>?i).
求中間經過所有點恰好一次的最小代價。
【算法】動態規劃
【題解】很巧妙的DP狀態設計。(這樣類似哈密頓路徑的問題不能從圖論方面考慮,否則很容易變成NP問題)
將代價拆分到每個點:
向左,起c+x,落b-x
向右,起d-x,落a+x
那麽對於前i個點,有效信息只有這i個點中缺少多少入邊和缺少多少出邊。先無視s(起點)和t(終點),那麽缺入邊數和缺出邊數相等。
令f[i][j]表示前i個點中缺少j條入/出邊的最小代價,缺少入邊的本質是被往左,缺少出邊的本質是往右。
對於f[i-1][j],有以下四種轉移:
被往右+往右:j>0,f[i][j]=f[i-1][j]+a[i]+d[i](兩個x[i]抵消)——減少一條出邊,同時增加一條出邊
往左+被往左:j>0,f[i][j]=f[i-1][j]+b[i]+c[i]——減少一條入邊,同時增加一條入邊
被往右+往左:j>0,f[i][j-1]=f[i-1][j]+a[i]+c[i]+2*x[i]——減少一條入邊和一條出邊
往右+被往左:f[i][j+1]=f[i-1][j]+b[i]+d[i]-2*x[i]——增加一條入邊和一條出邊
最終答案為f[n][0]。
接下來解決s和t的問題,實際上s和t才能代表一個完整的點,所以將s當成一個完整的點,不把t看作一個點。
先經過s:會多一條不該有的入邊,所以只要避免第二個和第三個轉移。
先經過t:會少一條該有的入邊,所以只要在j=0時強制進行第二個轉移。
最後在到達n之前和st均有或均無時,不能成環,強制f[i][0]=inf。
復雜度O(n^2)。
#include<cstdio> #include<cstring> #include<algorithm> #defineView Codell long long using namespace std; const ll inf=1e16,maxn=5010; ll f[maxn][maxn]; int n,s,t,x[maxn],a[maxn],b[maxn],c[maxn],d[maxn]; void m(ll &a,ll b){if(a>b)a=b;} ll solve(){ memset(f,0x3f,sizeof(f)); int tot=0;f[0][0]=0; for(int i=1;i<=n;i++){ for(int j=0;j<=i;j++)if(f[i-1][j]<inf){ int S=j,T=S+tot; if(i==s){ if(T)m(f[i][j],f[i-1][j]+c[i]+x[i]); m(f[i][j+1],f[i-1][j]+d[i]-x[i]); } else if(i==t){ if(S)m(f[i][j-1],f[i-1][j]+a[i]+x[i]); m(f[i][j],f[i-1][j]+b[i]-x[i]); } else{ if(S)m(f[i][j],f[i-1][j]+a[i]+d[i]); if(T)m(f[i][j],f[i-1][j]+b[i]+c[i]); if(S&&T)m(f[i][j-1],f[i-1][j]+a[i]+c[i]+2*x[i]); m(f[i][j+1],f[i-1][j]+b[i]+d[i]-2*x[i]); } } if(i==s)tot--;if(i==t)tot++; if(i!=n&&tot==0)f[i][0]=inf; } return f[n][0]; } int main(){ scanf("%d%d%d",&n,&s,&t); for(int i=1;i<=n;i++)scanf("%d",&x[i]); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++)scanf("%d",&b[i]); for(int i=1;i<=n;i++)scanf("%d",&c[i]); for(int i=1;i<=n;i++)scanf("%d",&d[i]); printf("%lld",solve()); return 0; }
PS:我在這場比賽進行Virtual participation的時候,大力貪心出B……然後排名好高啊><。
至今無人能證明但AC了的貪心(似乎有人給反例):初始s-t,然後考慮一個一個點找最優位置插入。
【CodeForces】704 B. Ant Man