【BZOJ】3502 PA2012 Tanie linie

【算法】贪心,一般DP

【题解】

---

胡策k≤10的环状DP做法:

1.钦定法:先确定第一位(可能和第n位)的状态,然后后面正常做DP,显然正确答案是一定会被记录的,因为从整体上看不会有影响。

2.环的特性:取的段和不取的段数量相等,位置互补。所以1和n的连接处都选或都不选都会有不被包括的情况,一选一不选就和链一样了。

---

正解贪心:

因为相邻的正数或相邻的负数肯定是要选一起选,所以点缩成正负正负…的数列形式,那么考虑先选择全部正的。

如果选择的段数过多,考虑删除则有两种选择:舍弃一个正数(相当于舍弃一个正数和两个负数,把这三个数视为一个负数),选择一个负数(相当于选择一个负数和两个正数,把这三个数视为一个正数)。

那么显然这两种行为是等价的:都是合并相邻的三个数字,可以视为对绝对值最小的数字操作最优,然后过程中用堆动态维护即可。

非环的情况特殊处理:不可能选择头尾的负数,因为没办法合并两个正数,过程中需要判断这个,而且这个头尾还会变化。

 

对拍还是查找错误相当重要的方式!考试一定要写暴力拍!

据说可以开头特判一些东西跳掉没用的数据——数据是死的,人是活的,出题人是懒的。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=1000010;

long long a[maxn],n,m,k,b[maxn],pre[maxn],suc[maxn];
bool f[maxn];
struct cyc
{
    long long num,ord;
    bool p;
    bool operator <(const cyc &a) const
    {
        return num>a.num;
    }
}qp;
priority_queue<cyc>q;
int main()
{
    
//    freopen("input","r",stdin);
    
    scanf("%lld%lld",&n,&k);
    bool now=0;long long tot,m=0;
    long long ans=0;
    scanf("%lld",&a[1]);
    b[(tot=1)]=a[1];now=a[1]>0;
    for(int i=2;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        if(a[i]>0&&!now)
        {
            now=1;
            b[++tot]=a[i];
        }
        else if(a[i]<=0&&now)
        {
            now=0;
            b[++tot]=a[i];
        }
        else b[tot]+=a[i];
    }
//    if((b[tot]>0)==(b[1]>0)&&tot!=1){b[1]+=b[tot];tot--;}//操作顺序 
    for(int i=1;i<=tot;i++)
    {
        pre[i]=i-1;suc[i]=i+1;
        if(b[i]>0){ans+=b[i];m++;}
        q.push((cyc){(b[i]>0?b[i]:-b[i]),i,b[i]>0});
    }
//    pre[1]=tot;suc[tot]=1;
    suc[tot]=0;
    long long en=tot,be=1;
    for(int i=m;i>k;i--)
    {
        while(f[q.top().ord]||((q.top().ord==be||q.top().ord==en)&&!q.top().p))q.pop();
        qp=q.top();q.pop();
        long long sum=0;long long A=pre[qp.ord],B=suc[qp.ord];
        if(qp.p)sum=qp.num+b[A]+b[B];
        else sum=b[A]+b[B]-qp.num;
        ans-=qp.num;
        if(suc[pre[A]])suc[pre[A]]=qp.ord;pre[qp.ord]=pre[A];
        if(pre[suc[B]])pre[suc[B]]=qp.ord;suc[qp.ord]=suc[B];
        f[A]=f[B]=1;b[qp.ord]=sum;
        //printf("ord=%lld num=%lld sum=%lld\n",qp.ord,qp.num,sum);
        if(B==en)en=qp.ord;
        if(A==be)be=qp.ord;
        q.push((cyc){(sum>0?sum:-sum),qp.ord,sum>0});
    }
    printf("%lld",ans);//开long long 
    return 0;
}
View Code

 

posted @ 2017-06-27 20:13  ONION_CYC  阅读(467)  评论(0编辑  收藏  举报