Codeforces 883D. Packmen Strike Back(二分+DP)
阿新 • • 發佈:2018-08-11
using lse com string not 所有 vector i+1 得到
傳送門
題意:一條長為n線上有幾個吃豆人和幾個豆子,你可以控制吃豆人的移動方向,選定方向之後吃豆人便會一直向這個方向走,問能吃到的最大豆子數和為了達到這個目標所需的最少時間。
思路:最大的豆子數量其實就是所有的豆子,唯一有個特例就是只有一個人,有兩個人以上的話,不難想出只要兩人面對面走就能吃到所有豆子了,所以先處理只有一個人的情況。
void straight() { int sta=pos[0]; int sumlef=0,timlef=0,k=0,sumrig=0,timrig=0; for(int i=sta-1;i>=1;i--) { k++; if(s[i]=='*') { sumlef++; timlef=k; } } k=0; for(int i=sta+1;i<=n;i++) { k++; if(s[i]=='*') { sumrig++; timrig=k; } } if(sumlef<sumrig || (sumlef==sumrig && timrig<timlef)) cout<<sumrig<<" "<<timrig<<endl; else cout<<sumlef<<" "<<timlef<<endl; }
然後就是怎麽求最小時間了,數據範圍是1e6,所以應該是O(nlogn)或O(n)的算法,再加上這道題明顯符合單調性(X秒內能吃到Y顆豆子,那麽X+1秒內一定可以吃到Y顆豆子)所以我們可以二分時間。
void binary() { int l=0,r=n,mid,ans=0; while(l<=r) { mid=(l+r)/2; if(check(mid)) { r=mid-1; ans=mid; } else l=mid+1; } cout<<sum[n]<<" "<<ans<<endl; }
然後就是如何check的問題,我們可以用DP來解決,dp[i]表示前i個人能吃到的從左數最右邊的豆子(就是前i個人能吃到的最右邊的豆子),dp[i]可以從dp[i-1]再加上第i個人吃的豆子轉移得到,但一共有三種情況,取最大值。
一、第i個人向左走。
二、第i個人向右走。
三、第i-1個人向右,第i個人向左。
分別算就行了,當然,為了求第i個人到第i-1個人之見豆子的數量,要提前預處理好前綴和。
void prework() { for(int i=1;i<=n;i++) { if(s[i]=='*') sum[i]=sum[i-1]+1; else sum[i]=sum[i-1]; if(s[i]=='P') pos.pb(i); } m=pos.size(); } inline bool nothing(int l,int r) { return (r<l || !(sum[r]-sum[l-1])); } bool check(int tim) { memset(dp,0,sizeof(dp)); for(int i=0;i<m;i++) { if(nothing(dp[i]+1,pos[i]-tim-1)) dp[i+1]=max(dp[i+1],pos[i]); if(nothing(dp[i]+1,pos[i]-1)) dp[i+1]=max(dp[i+1],pos[i]+tim); if(i>=1 && nothing(dp[i-1]+1,pos[i]-tim-1) && pos[i]-tim<pos[i-1]) dp[i+1]=max(dp[i+1],pos[i-1]+tim); } if(nothing(dp[m]+1,n)) return 1; return 0; }
最後是完整的AC代碼
//頭文件日常省略
using namespace std;
const int maxn=1000005;
string s;
int n,m;
vector<int> pos;
int sum[maxn],dp[maxn];
void prework()
{
for(int i=1;i<=n;i++)
{
if(s[i]=='*')
sum[i]=sum[i-1]+1;
else
sum[i]=sum[i-1];
if(s[i]=='P')
pos.pb(i);
}
m=pos.size();
}
void straight()
{
int sta=pos[0];
int sumlef=0,timlef=0,k=0,sumrig=0,timrig=0;
for(int i=sta-1;i>=1;i--)
{
k++;
if(s[i]=='*')
{
sumlef++;
timlef=k;
}
}
k=0;
for(int i=sta+1;i<=n;i++)
{
k++;
if(s[i]=='*')
{
sumrig++;
timrig=k;
}
}
if(sumlef<sumrig || (sumlef==sumrig && timrig<timlef))
cout<<sumrig<<" "<<timrig<<endl;
else
cout<<sumlef<<" "<<timlef<<endl;
}
inline bool nothing(int l,int r)
{
return (r<l || !(sum[r]-sum[l-1]));
}
bool check(int tim)
{
memset(dp,0,sizeof(dp));
for(int i=0;i<m;i++)
{
if(nothing(dp[i]+1,pos[i]-tim-1))
dp[i+1]=max(dp[i+1],pos[i]);
if(nothing(dp[i]+1,pos[i]-1))
dp[i+1]=max(dp[i+1],pos[i]+tim);
if(i>=1 && nothing(dp[i-1]+1,pos[i]-tim-1) && pos[i]-tim<pos[i-1])
dp[i+1]=max(dp[i+1],pos[i-1]+tim);
}
if(nothing(dp[m]+1,n))
return 1;
return 0;
}
void binary()
{
int l=0,r=n,mid,ans=0;
while(l<=r)
{
mid=(l+r)/2;
if(check(mid))
{
r=mid-1;
ans=mid;
}
else
l=mid+1;
}
cout<<sum[n]<<" "<<ans<<endl;
}
int main()
{
cin>>n>>s;
s=" "+s;
prework();
if(m==1)
straight();
else
binary();
return 0;
}
Codeforces 883D. Packmen Strike Back(二分+DP)