2017.10.20 T1 1997
样例数据
输入
5
4 6 2 8 4
1 5 7 9 2
输出
4
分析:之前做过重温世界杯,这道题我就用的类似的dp思想。其实加上了“A总和等于B总和”这个条件,题目是变简单了的:只需要算出所有卡收入的前缀和,最小前缀和的卡为答案。
为什么呢?因为前缀和最小的卡之后的卡的收入总和是等于这个最小值的相反数的(因为收入总和为B-A=0),所以从它的后一个开始取就相当于给所有前缀和加上了一个这个最小值的相反数,而且由于这是最小值,所以前面的那些负数都绝对被加成了正数,故可以取完所有刮刮卡。(其实就是最后拿那个如果从头拿会亏本最厉害的,这样相当于取到它之前都是赚,取完它正好不赚不亏)
举个例子:
B: 1 3 8 2 4 6 5
A: 2 3 3 8 1 5 7
收入:-1 0 5 -6 3 1 -2
从开头开始算前缀和:
-1 -1 4 -2 1 2 0
我们发现地四张卡前缀和-2最小,所以就从它的后一个开始,这样算出的前缀和是这样的(从第五张卡开始):
3 4 2 1 1 6 0
相当于给所有的前缀和加上了2,因为最小负数是-2,所以前面1—4张卡前缀和就没有负数了,所有卡都能被取到。
代码
我的dp思想(基本和重温世界杯一模一样)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;
int getint()
{
int sum=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
const int maxn=1000010;
struct node{
int c,w;
}dp[maxn*2];
int n,sum,ans,res,bh;
int a[maxn],b[maxn];
int main()
{
freopen("rock.in","r",stdin);
freopen("rock.out","w",stdout);
n=getint();
for(int i=1;i<=n;++i)
{
b[i]=getint();
sum+=b[i];
}
for(int i=1;i<=n;++i)
{
a[i]=getint();
dp[i].c=b[i]-a[i];
dp[i+n].c=dp[i].c;
dp[i].w=b[i];
dp[i+n].w=dp[i].w;
}
bh=0;
for(int i=2;i<=2*n;++i)
{
if(dp[i].c+dp[i-1].c>=0&&dp[i-1].c>=0)
{
dp[i].c=dp[i].c+dp[i-1].c;
dp[i].w=dp[i].w+dp[i-1].w;
if(dp[i].w==sum)
{
ans=sum;
res=bh;
break;
}
}
else if(dp[i].c+dp[i-1].c<0&&dp[i-1].c>=0)
{
if(ans<dp[i].w+dp[i-1].w)
{
res=bh;
ans=dp[i].w+dp[i-1].w;
}
bh=i;
}
else if(dp[i-1].c<0)
{
if(ans<dp[i-1].w)
{
res=bh;
ans=dp[i-1].w;
}
bh=i-1;
}
}
cout<<res<<'\n';
return 0;
}
简单的正解
#include <bits/stdc++.h>
using namespace std;
const int Max=1001000;
int n,ans,sum;
int a[Max],b[Max],c[Max],dis[Max];
int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(c<'0'||c>'9')&&(c!='-');c=getchar());
if(c=='-') {c=getchar();f=-1;}
for(;c>='0'&&c<='9';c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
int main()
{
freopen("rock.in","r",stdin);
freopen("rock.out","w",stdout);
n=get_int();
for(int i=1;i<=n;i++) b[i]=get_int();
for(int i=1;i<=n;i++) a[i]=get_int();
for(int i=1;i<=n;i++) c[i]=b[i]-a[i];
dis[1]=c[1];
for(int i=2;i<=n;i++) dis[i]=dis[i-1]+c[i];
int minn=1e+8,location;
for(int i=1;i<=n;i++) if(minn>dis[i]) minn=dis[i];
for(int i=1;i<=n;i++) if(minn==dis[i]) {location=i;break;}
cout<<location<<endl;
return 0;
}
本题结。