Codeforces Packmen Strike Back (二分+dp)
题目链接:
http://codeforces.com/contest/883/problem/D
题意:
给一个字符串,P代表人,每个人往左走到底或者往右走到底,问最多获得多少星号,在这个前提下最少走多少秒收集完。
思路:
二分答案,check的时候看前i个人最多可以收集到哪个位置。
转移有三种情况
1. 第i个人往左走,但无法收集完第i-1个人收集的星号,无法让i-1转向
2. 第i个人往左走,可以收集完第i-1个人收集的星号,可以让i-1转向
3. 第i个人往右走,左边都收集完
思维难度还是有点大
#include<bits/stdc++.h> using namespace std; const int N=1000005; char s[N]; int sum[N],a[N]; int tot; int R[N]; int n; bool have(int r,int l) { l=max(0,l); if (l>=r) return 0; return sum[r]-sum[l]>0; } int f[N]; bool check(int len) { f[0]=0; a[0]=0; for (int i=1;i<=tot;i++) { f[i]=0; if (!have(a[i]-len-1,f[i-1])) f[i]=a[i]; if (i>=2&&!have(a[i]-len-1,f[i-2])) f[i]=max(f[i],a[i-1]+len); if (!have(a[i],f[i-1])) f[i]=max(f[i],a[i]+len); if (f[i]==0) return 0; } if (have(n,f[tot])) return 0; return 1; } int main() { scanf("%d",&n); scanf("%s",s+1); for (int i=1;i<=n;i++) { sum[i]=sum[i-1]; if (s[i]=='*') sum[i]++; if (s[i]=='P') a[++tot]=i; } if (tot==0) printf("0 0"); else { if (tot==1) { int cnt1=0,cnt2=0,dl=0,dr=0; for (int i=1;i<=n;i++) if (s[i]=='*') { if (i<a[1]) cnt1++,dl=max(dl,a[1]-i); else cnt2++,dr=max(dr,i-a[1]); } if (cnt1>cnt2) printf("%d %d",cnt1,dl); if (cnt1<cnt2) printf("%d %d",cnt2,dr); if (cnt1==cnt2) printf("%d %d",cnt1,min(dl,dr)); } else { int cnt=0; for (int i=1;i<=n;i++) if (s[i]=='*') cnt++; R[n+1]=n+1; for (int i=n;i>=1;i--) if (s[i]=='*') R[i]=i; else R[i]=R[i+1]; int l=0,r=n; while(l<r) { int mid=(l+r)/2; if (!check(mid)) l=mid+1; else r=mid; } printf("%d %d",cnt,l); } } }