题目链接:
http://acm.zju.edu.cn/changsha/showProblem.do?problemCode=J
题目意思:
有n个人,每个人有一个数(>=0)。给出n个数,-1表示该人所持有的数未知,否则为该人所持有的数。
给出n个数p[n],p[i]表示当前人为中心的三个连续的数的和。有m种查询t,查询第t个人能够持有的最大数。
解题思路:
数学+模拟。
假设n个数分别为a1,a2,a3....an ,将开始和最后一个数记为0,a[0]=a[n+1]=0;
则有
a0+a1+a2=p0
a1+a2+a3=p1
a2+a3+a4=p2
.....
an-1+an+an+1=pn-1
显然p1-p2=a3,a6-a3=p4-p3...所以可以推得a[i%3==0])。同理从后往前可以推得a(n-2)=p(n-2)-p(n-1) 如果(n-2)%3不为零或者某一个a[i]!=-1(i%3不为0),则可以全部推出——从该位置从前往后和从后往前各一次。
否则的话,至于%3==1和%3==2的都没有推出来,但是ai-a(i+3)=pi-p(i+1)为定值,a1,a4,a7....增减性相同,同为最大,同为最小。有a1+a2为定值,所以%3==1和%3==2的增减性相反。
分别求出两种余数的最大值。
对于i%3==1的情况,可以假设a[1]它为最大值p[0],则a[i%3==1]均为最大值,但,要保证a[i%3==2]>=0,所以就统计a[i%3==2]的最小值tmp,如果tmp<0,则它增加到0,所有的a[i%3==1]减去abs(tmp).
同理可以求出 a[i%3==2]的最大值。
PS:尽最大可能找出题目的特点,性质。
求相关的最小值贪心有问题,见代码后面样例。
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define Maxn 110000
int a[Maxn],p[Maxn];
int Ma[Maxn];
int tt[Maxn];
int main()
{
int n;
// printf("%d\n",INF);
while(~scanf("%d",&n))
{
bool flag=false;
int re;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]==-1) //不确定
continue;
if(i%3&&i%3!=(n-2)%3) //有其他的,可以全部推出
{
flag=true;
re=i;
}
}
a[0]=a[n+1]=0;
if((n-2)%3) //不为零,可以全部推出
{
flag=true;
re=n-2;
}
for(int i=0;i<n;i++)
scanf("%d",&p[i]);
for(int i=3;i<=n;i+=3) //求出a[i%3==0]
a[i]=p[i-2]-p[i-3]+a[i-3];
if((n-2)%3) //从后往前,依次求出
{
for(int i=n-2;i>=1;i-=3)
a[i]=p[i]-p[i+1]+a[i+3];
}
if(flag) //可以全部推出来
{
for(int i=re;i<n;i++) //>=re的都知道了,
{
if(i%3==1)
a[i+1]=p[i]-a[i]-a[i+2];
else if(i%3==2)
a[i+2]=p[i]-a[i+1]-a[i];
else
a[i+2]=p[i]-a[i]-a[i+1];
}
for(int i=re;i>=1;i--) //求<=re的 后面已经已知了
a[i-1]=p[i-1]-a[i]-a[i+1];
}
if(!flag)
{
memcpy(tt,a,sizeof(a));
Ma[1]=p[0]; //置为最大
tt[1]=p[0];
int res=0;
for(int i=2;i<=n;i++)
{
if(i%3==0)
continue;
int tmp=p[i-2]-tt[i-2]-tt[i-1];
tt[i]=tmp;
if(i%3==1)
Ma[i]=tmp;
else if(tmp<res) //统计%3==2的最小值
res=tmp; //
}
if(res<0) //保证a[i%3==2]>=0
{
for(int i=1;i<=n;i+=3)
Ma[i]+=res;
}
Ma[2]=p[0];
memcpy(tt,a,sizeof(a));
tt[2]=p[0];
tt[1]=0;
res=0;
for(int i=4;i<=n;i++)
{
if(i%3==0)
continue;
tt[i]=p[i-2]-tt[i-1]-tt[i-2];
if(i%3==2)
Ma[i]=tt[i];
else if(tt[i]<res)
res=tt[i];
}
if(res<0)
{
for(int i=2;i<=n;i+=3)
Ma[i]+=res;
}
}
int m;
scanf("%d",&m);
while(m--)
{
int tmp;
scanf("%d",&tmp);
tmp++;
if(flag) //已经全部推出了
printf("%d\n",a[tmp]);
else
{
if(tmp%3==0) //可以推出来的
printf("%d\n",a[tmp]);
else
printf("%d\n",Ma[tmp]); //能够取得的最大值
}
}
}
return 0;
}
/*
5
1 -1 -1 -1 -1
2 3 4 3 2
5
0 1 2 3 4
*/
/*
比如
a1+a2=3
a2+a3=4
a3+a4=5
a4+a5=3
的话,a2按照求相关的两个方程最小的话为3 但是推得a5=-1
其实 a2可以为2的。
*/