P3210 [HNOI2010]取石头游戏

题目链接

博弈论好题!

但是还没有完全弄懂...

主要的思想就是判掉“上三角”的情况,因为这种情况看起来比较劣,大家会把它放最后再选,并且一定会连续地选,即“先手”选第一个和第三个,“后手”选第二个.(理性理解失败,只能感性理解)

处理掉“上三角”以后,就可以直接从大到小选了。

然后会发现样例都 WA 了。这是因为边界可能出现靠边的位置比较大的情况,而这时如果有偶数个,那么肯定留在最后“被迫”选择;如果有奇数个,那么最靠里的那个不会受到影响。

值得注意的是,两边还可能出现“下三角”的情况,这时需要我们先拿走“向下”的那部分。

代码(栈):

signed main() {
	read(n);
	for (register int i = 1; i <= n; ++i) {
		read(v[i]);
		vis[i] = v[i] > 0;
		if (vis[i])	++tot;
		sum += v[i];
	}
	bool flag = false;
	for (register  int i = 1; i <= n; ++i) {
		if (!vis[i]) {
			if (!flag) {
				flag = true;
				memcpy(tmp, stk, sizeof(stk));
				memset(stk, 0, sizeof(stk));
				top = stop; stop = 0;
			} else {
				while (stop)	h[++htot] = stk[stop], stop--;
			}
			continue;
		}
		stk[++stop] = v[i];
		while (stop > 2 && stk[stop - 1] >= stk[stop] && stk[stop - 1] >= stk[stop - 2])
			--stop, stk[stop] = stk[stop + 1] + stk[stop - 1] - stk[stop], --stop, stk[stop] = stk[stop + 1];
	}
	
	
	while (top > 1 && tmp[top] >= tmp[top - 1])	h[++htot] = tmp[top], --top;
	if (top <= 1) {
		while (top)	h[++htot] = tmp[top], top--;
	} else {
		for (register int i = 1; i + 1 <= top; i += 2) {
			cha += (tot & 1 ? 1 : -1) * (tmp[i] - tmp[i + 1]);
		}
		if (top & 1)	h[++htot] = tmp[top];
	}
	int st = 1;
	while (st < stop && stk[st] >= stk[st + 1])	h[++htot] = stk[st], ++st;
	if (stop - st + 1 <= 1) {
		while (stop >= st)	h[++htot] = stk[stop], --stop;
	} else {
		for (register int i = stop; i - 1 >= st; i -= 2) {
			cha += (tot & 1 ? 1 : -1) * (stk[i] - stk[i - 1]);//Attention!!!!!
		}
		if ((stop - st + 1) & 1)	h[++htot] = stk[st];
	}
	sort(h + 1, h + 1 + htot);
	int tp = 1;
	for (register int i = htot; i; --i) {
		cha += tp * h[i];
		tp *= -1;
	}
	printf("%lld %lld\n", (cha + sum) / 2, (sum - cha) / 2);
	return 0;
}

刘队的简化版代码(双向链表):

int main()
{
    read(n),r[0]=1,l[n+1]=n;
    for(int i=1;i<=n;++i)
        read(v[i]),sum+=v[i],l[i]=i-1,r[i]=i+1,tag[i]=(v[i]!=0);
    for(int i=3;i<=n;i=r[i])
        while(tag[l[l[i]]]&&tag[l[i]]&&tag[i]&&v[l[i]]>=v[l[l[i]]]&&v[l[i]]>=v[i])
            v[i]=v[l[l[i]]]+v[i]-v[l[i]],r[l[l[l[i]]]]=i,l[i]=l[l[l[i]]];
    L=r[0],R=l[n+1];
    while(v[L]>=v[r[L]]&&tag[L]&&tag[r[L]]) s+=v[r[L]]-v[L],L=r[r[L]];
    while(v[R]>=v[l[R]]&&tag[R]&&tag[l[R]]) s+=v[l[R]]-v[R],R=l[l[R]];
    for(int i=L;i<=R;i=r[i])
        if(tag[i])
            v[++tot]=v[i];
    sort(v+1,v+tot+1,cmp),v[++tot]=s;
    for(int i=1;i<=tot;++i)
    {
        if(i&1) val+=v[i];
        else val-=v[i];
    }
    printf("%lld %lld",(sum+val)/2,(sum-val)/2);
    return 0;
}
posted @ 2020-08-05 11:24  JiaZP  阅读(141)  评论(0编辑  收藏  举报