猫 题解

猫 题解

有一个环上有\(n\)个节点(猫)。每删去一个节点\(i\),就会获得其权值\(w_i\),但是节点\(i-1\)\(i+1\)(注意是环状的,\(n+1\)号节点相当于\(1\)号,\(0\)号相当于\(n\)号)就不能被删去了。

\(\forall m\in[1,n/2]\cap \mathbb{N},\)刚好杀掉\(m\)只猫可以得到的最大权值之和。

Solution

想先到了一种中规中矩的方法,设\(dp[i][j][0/1][0/1]\)代表前\(i\)只猫中,正好消灭的\(j\)只猫时,已经/没有消灭第\(i\)只猫,已经/没有消灭第\(1\)只猫的最优权值之和就好,复杂度\(O(n^2)\)

int main()
{
//	freopen("cat.in","r",stdin);
//	freopen("cat.out","w",stdout);
	n=read();
	for(re int i=1;i<=n;i++) a[i]=read();
	f[1][0][0][0]=0;
	f[1][1][1][1]=a[1];
	for(re int i=2;i<=n;i++)
	{
		for(re int j=1;j<=(i>>1);j++)
		{
			f[i][j][0][0]=max(f[i-1][j][0][0],f[i-1][j][1][0]);
			f[i][j][0][1]=max(f[i-1][j][0][1],f[i-1][j][1][1]);
			f[i][j][1][0]=f[i-1][j-1][0][0]+a[i];
			f[i][j][1][1]=f[i-1][j-1][0][1]+a[i];
		}
	}
	for(re int j=1;j<=n/2;j++)
	{
		write(max(f[n][j][1][0],max(f[n][j][0][1],f[n][j][0][0])));
	}
	return 0;
}

当然还可以做一些无用的滚动数组

换个想法考虑一下。

如果消灭一只猫的收益大于与其相邻两只猫的收益时,那么假如不消灭这只猫,就一定要消灭与他相邻的两只猫。所以我们考虑反悔贪心,消灭一只猫时把它相邻的两只猫缩成一只猫,新猫的权值为\(相邻两只猫的权值和-中间的猫的权值\)。假如再消灭缩出来的那只猫就相当于只消灭相邻的猫的收益且消灭的猫数加\(1\)

所以我们用堆贪心地选择较大的那一个,将两边删去,插入新猫即可。

实时用一个环状链表和堆维护就好。

\(O(n\log n)\)

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#ifdef TH
#define debug printf("Now is %d\n",__LINE__);
#else
#define debug
#endif
using namespace std;

template<class T>inline void read(T&x)
{
    char ch=getchar();
    int fu;
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') fu=-1,ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    x*=fu;
}
inline int read()
{
	int x=0,fu=1;
    char ch=getchar();
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') fu=-1,ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*fu;
}
int G[55];
template<class T>inline void write(T x)
{
    int g=0;
    if(x<0) x=-x,putchar('-');
    do{G[++g]=x%10;x/=10;}while(x);
    for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
int n;
int l[200010],r[200010];
struct cat
{
	LL x,y;
	bool operator<(const cat & z)const
	{
		return x<z.x;
	}
}t;
LL ans,a[200010];
priority_queue<cat>q;
bool book[200010];
IL void work(int x)
{
	book[x]=1;
	l[r[x]]=l[x];
	r[l[x]]=r[x];
}
int main()
{
//	freopen("cat.in","r",stdin);
//	freopen("cat.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++)
	{
		t.x=a[i]=read();
		t.y=i;
		q.push(t);
		if(i==1) l[i]=n;
		else l[i]=i-1;
		if(i==n) r[i]=1;
		else r[i]=i+1;
	}
	n>>=1;
	while(n--)
	{
		t=q.top();
		while(book[t.y]) q.pop(),t=q.top();
		q.pop();
		ans+=t.x;
		t.x=a[t.y]=a[l[t.y]]+a[r[t.y]]-t.x;
		q.push(t);
		work(l[t.y]);
		work(r[t.y]);
		write(ans);
	}
	return 0;
}

End

我还以为\(dp\)一般都是正解了呢……我太菜了,好不容易搞出了\(dp\),结果还有人上人——\(so\)\(lution\)啊……

posted @ 2020-11-22 09:36  Vanilla_chan  阅读(136)  评论(0)    收藏  举报