前缀和,枚举+递推(Average,HDU 5353)

来源:互联网 发布:淘宝全新店铺购买 编辑:程序博客网 时间:2024/05/18 01:20

有n个节点,1<=n<=1e5,成环,每个节点有一些物品,相邻节点可以传递一次物品,1和n相邻。

瞬间就想起了

Money Transfers


这类问题一般都可以转化为环的切割,如何求出切割呢?

可以求一个前缀和,如果sum[j]==sum[i](i<j)那么[i+1,j]就可以切割成一段,首尾剩下的也可以并成一段。

我们会找到很多个sum的值,每一种值对应一个切割方法,值所对应的的多个节点就都是切割点。


在本题我们就通过前缀和找到所有切割,然后对于每一段,可以直接贪心模拟看看是否ok。


看了下官方题解,是枚举第一个人的操作,然后递推出其他人的操作。

因为环状的问题要同时兼顾两边,所以没有什么简单的算法可以得到答案。

但是如果能在环上切一刀,那么问题就变成了一个线段上的问题,这个时候问题会大大化简,我们甚至可以直接贪心递推来得到答案。

即枚举+递推。


本题代码

#include<stdio.h>#include<map>#include<vector>#include<algorithm>using namespace std;typedef long long ll;const int maxn = 100010;int n;int a[maxn<<1];int sum[maxn];ll SUM;map<int,int>MAP;int cnt;vector<int>vec[maxn];vector<int>ans;void read(){    MAP.clear();    cnt=0;    SUM=0;    scanf("%d",&n);    for(int i=0;i<n;i++)    {        scanf("%d",a+i);        SUM+=a[i];    }}int id(int x){    if(!MAP.count(x))    {        MAP[x]=++cnt;        vec[cnt].clear();    }    return MAP[x];}bool OK(int l,int r){    if(r-l<=1) return true;    int jin=0;    for(int k=l+1;k<=r;k++)    {        int x = a[k]+jin;        if(x>=2||x<=-2) return false;        if(x==1)        {            ans.push_back(k);            ans.push_back(k+1);        }        else if(x==-1)        {            ans.push_back(k+1);            ans.push_back(k);        }        jin=x;    }    return !jin;}bool ok(int x){    ans.clear();    for(int i=0;i<(int)vec[x].size()-1;i++)        if(!OK(vec[x][i],vec[x][i+1]))            return false;    if(!OK(vec[x][(int)vec[x].size()-1],vec[x][0]+n))        return false;    return true;}void print(){    puts("YES");    printf("%d\n",ans.size()/2);    for(int i=0;i<(int)ans.size();i+=2)        printf("%d %d\n",ans[i]%n+1,ans[i+1]%n+1);}int solve(){    read();    if(SUM%n) return 0*puts("NO");    SUM/=n;    for(int i=0;i<n;i++)    {        a[i]-=SUM;        if(a[i]>2||a[i]<-2) return 0*puts("NO");        a[i+n]=a[i];        sum[i]=a[i];        if(i) sum[i]+=sum[i-1];    }    for(int i=0;i<n;i++)        vec[id(sum[i])].push_back(i);    for(int i=1;i<=cnt;i++)        if(ok(i))        {            print();            return 0;        }    puts("NO");    return 0;}int main(){    int T;    scanf("%d",&T);    while(T--) solve();    return 0;}

Money Transfers代码

#include<stdio.h>#include<algorithm>#include<map>using namespace std;typedef long long ll;const int maxn = 100010;int n;ll sum[maxn];map<ll,int>cnt;int main(){    scanf("%d",&n);    for(int i=0;i<n;i++) scanf("%lld",sum+i);    for(int i=1;i<n;i++) sum[i]+=sum[i-1];    for(int i=0;i<n;i++) cnt[sum[i]]++;    int MAX=0;    for(map<ll,int>::iterator it = cnt.begin();it!=cnt.end();++it) MAX=max(MAX,it->second);    printf("%d\n",n-MAX);    return 0;}


原创粉丝点击