20180716 [AtCoder]CF697 EASY+SoundHound HARD【值得琢磨实现细节】
A - Romaji CodeForces - 1008A
B - Turn the Rectangles CodeForces - 1008B
C - Reorder the Array CodeForces - 1008C
D - Saving Snuuk AtCoder - 4190
E - + Graph AtCoder - 4191 (留坑,至今未果,寻病终)
A
题目简述:
把一个数组分成连续的k段,每一段的价值为这一段最大元素的值,求每一段价值之和的最大值。并输出每一段的长度。
分析:
贪心地思考一下,这个数组无论如何都可以分成k段(n>=k)则将这个数组前k大的数字分在k个部分中 问题就在于怎么输出 细节考虑比较多 考试的时候实现得不是很好 也卡了很久
//考试的实现
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 2005
int N,K,ans,cn;
struct node{
int v,p;
}a[MAXN],c[MAXN];
int b[MAXN];
bool cmp(node x,node y)
{
return x.v>y.v;
}
bool cmp2(node x,node y)
{
return x.p<y.p;
}
int main()
{
scanf("%d %d",&N,&K);
for(int i=1;i<=N;i++)
{
scanf("%d",&a[i].v);
a[i].p=i;
b[i]=a[i].v;
}
sort(a+1,a+N+1,cmp);
for(int i=1;i<=K;i++)
{
ans+=a[i].v;
c[++cn].v=a[i].v;
c[cn].p=a[i].p;
}
sort(c+1,c+cn+1,cmp2);
int n=1;
printf("%d\n",ans);
int last=0;
for(int i=1;i<=N;i++)
if(b[i]==c[n].v)
{
if(n==cn)
{
printf("%d",N-last);
return 0;
}
printf("%d ",i-last);
last=i;
n++;
}
}
就是保存了前k大的数在数组中的位置,输出的时候以此分节,但却是实现不够优秀(包括最后的for循环其实多余了 都已经存有了位置 for 1~k 就OK)。
//更优秀的实现
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 2000;
struct node{
int val, id;
}a[MAXN + 5];
bool operator < (node a, node b) {
return a.val < b.val;
}
int b[MAXN + 5], bcnt;
int main() {
int n, k;
scanf("%d%d", &n, &k);
for(int i=1;i<=n;i++) {
scanf("%d", &a[i].val);
a[i].id = i;
}`
sort(a+1, a+n+1);
int ans = 0;
for(int i=n;i>=n-k+1;i--) {
ans += a[i].val;
b[++bcnt] = a[i].id;
}
b[++bcnt] = n+1;
printf("%d\n", ans);
sort(b+1, b+bcnt+1);
printf("%d", b[2]-1);
for(int i=2;i<bcnt;i++)
printf(" %d", b[i+1]-b[i]);
}
B
题目简述:
把数组分成连续的3段 使第一段的元素之和等于第三段的元素之和 每一段都可以为空 求第一段元素之和的最大值
分析:
Tip:正解&简洁的代码请往下翻
我一拿到就想到了前缀和&后缀和
a[i]表示1~i的和 b[j]表示j~N的和 若i,j满足i < j并且a[i]==b[j] 更新ans
然后 我就这么写了 然后 T了QAQ
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 200006
int N,ans=-10;
int a[MAXN],b[MAXN],c[MAXN];
int main()
{
scanf("%d",&N);
for(int i=1;i<=N;i++)
{
scanf("%d",&a[i]);
b[i]=b[i-1]+a[i];
}
for(int i=N;i>=1;i--)
c[i]=c[i+1]+a[i];
for(int i=1;i<N;i++)
for(int j=i+1;j<=N;j++)
if(b[i]==c[j])
ans=max(ans,b[i]);
if(ans==-10) ans=0;
printf("%d\n",ans);
}
然后后来。。。搞了一个挺麻烦的操作。。。把前缀和后缀数组搞成了一个 标记了一下属性
然后就是 记得一定要开long long 10^9加起来就爆了 QAQ
如果是黑箱测试 我这场考试就凉凉了
//考场实现
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 200005
int N,ans;
int a[MAXN];
struct node{
long long v;
int p,t;
}b[2*MAXN];
bool cmp(node x,node y)
{
return x.v>y.v;
}
int main()
{
scanf("%d",&N);
for(int i=1;i<=N;i++)
{
scanf("%d",&a[i]);
b[i].v=b[i-1].v+a[i];
b[i].p=i;
b[i].t=1;
}
for(int i=N;i>=1;i--)
b[i+N].v=b[i+N+1].v+a[i],b[i+N].p=i,b[i+N].t=2;
sort(b+1,b+2*N+2,cmp);
for(int i=1;i<=2*N;i++)
if(b[i].v==b[i+1].v&&((b[i].p<b[i+1].p&&b[i].t==1&&b[i+1].t==2)||(b[i].p>b[i+1].p&&b[i].t==2&&b[i+1].t==1)))
{
printf("%lld\n",b[i].v);
return 0;
}
printf("0\n");
return 0;
}
//long long不开毁前程
然后这个差不多的思想 有一个特别简单的实现 (赛后shared)
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,t;
long long a[200001],ans;
int main() {
scanf("%d",&n);
for(int i=1; i<=n; i++)scanf("%I64d",&a[i]),a[i]+=a[i-1];
for(int i=1; i<=n; i++) {
t=lower_bound(a+1,a+1+n,a[n]-a[i])-a;
if(a[t]==a[n]-a[i]&&i<=t)ans=a[i];
}
printf("%lld",ans);
return 0;
}
赛后看了这个实现 我。。。QAQ
最关键的就是在第二个for循环里面
根本不需要另外用一个后缀数组 a[n]-a[i]==b[i] 然后low_bound()求出第一个大于等于这个数的位置(对于这道题而言,upper _bound也可以)
如果在前缀里面 能够找到与之相等的数 并且后缀的位置大于前缀 则合法 更新ans
这道题还有一个思路就是用滑动窗口的思想 设置左右边界 由于sum具有单调性(所有的元素都是正数),则可以通过调整边界来调整sum的关系
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define MAXN 200000
int n,d[MAXN+5],l,r;
long long sum1,sum2,ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&d[i]);
int l=0,r=n+1;
while(l<r)
{
if(sum1==sum2)ans=max(ans,sum1),l++,r--,sum1+=d[l],sum2+=d[r];
if(sum1<sum2)l++,sum1+=d[l];
if(sum1>sum2)r--,sum2+=d[r];
}
printf("%lld",ans);
}
C
题目简述:
2个长度为n的字符串 可以进行如下交换
1.a[i]与a[n-i+1]
2.a[i]与b[i]
3.b[i]与b[n-i+1]
可以将两个字符串 改变其中的字符 问至少要改变多少个字符才能使这2个字符串完全相等
分析:
有点模拟的意味 但不是纯暴力 进行分类讨论即可
注意a[i]与b[j]和b[i]与a[j]直接交换是非法的 需进行2次交换 交换后顺序与直接交换不同
然后 坑点来了
敲黑板
题目上说只能修改a的字符
如果 if(a[i]==a[j]) swap(a[i],b[i]),swap(a[i],a[j]);
这个操作就会把a[j]和b[i]换了
当后面访问到a[j]时如果你发现无法与b[j]匹配,后面的程序就会修改a[j]的值
但实际上题目要求是先修改值,再交换位置
所以修改a[j]就相当于修改b[j],就违反了规则
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 100005
char a[MAXN],b[MAXN];
int N,ans;
int main()
{
scanf("%d",&N);
scanf("%s\n%s",a+1,b+1);
for(int i=1;i<=N;i++)
if(a[i]!=b[i])
{
int j=N-i+1;
if(j>i)
{
if(a[i]==b[j]) swap(b[i],b[j]);
else if(b[i]==a[j]) swap(a[i],a[j]);
//else if(a[i]==a[j]) swap(a[i],b[i]),swap(a[i],a[j]);
else if(a[j]==b[j]) swap(a[i],a[j]),swap(b[i],b[j]);
else if(b[i]==b[j]) swap(a[i],b[i]),swap(b[i],b[j]);
else ans++;
}
else ans++;
}
printf("%d\n",ans);
}
还有一种做法,就是满足这样的一种性质:可以互相交换的4个地方的字符一定4个一样或者有2种字符,每种2个。
其实也比较好做,但是我没做。
D
题意简述:
Kenkoooo去旅行,有n个城市,m条火车线路。每条线路有两种权值:日元和Snuke,任选一种即可。开始时,他有10 ^15日元,在任意一个城市,他都可以将日元换成Snuke,但必须全部换完,且只能换一次,并且城市i会在i年后不能兑换货币。他每年都会去旅行,对于每一年求最大能够省下的Snuke。
思路分析:
很朴素的一个想法当然就是枚举在哪个车站换钱,但是肯定会超时。
怎么办呢?我们折中一下,因为不管在哪个车站换,在那个车站以前,用的是日元,在那个车站以后,用的是Snuke。
所以我们分别以起点和终点为起点,这话怎么这么绕,分别以日元和Snuke为边权,找最短路,然后枚举断点。
但是这道题的有(恶)趣(心)之处呢,就在于它有很多很多年,每一年的可供换钱的车站还有变化。年数要是太大,时间也不好受。
于是我们可以这样进行一个优化。
每一年会关掉i号窗口,如果我们把时间轴倒着看,就是每一年会多开第i号窗口。而对于第i年来说,由于变化的只是第i号窗口有没有开,所以对于上一年(实际是下一年,这里把时间轴倒着看的)求出的那个ans值,表示的是在i+1~n号窗口换的最优值,那我们只需将上一年的ans值,与这一年在第i号窗口换钱的值进行比较,取最小即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 100005
#define LL long long
#define INF 1e18
int N,M,S,T,u,v;
LL a,b,f=1e15,ans[MAXN];
LL Min(LL x,LL y)
{
if(x>y) return y;
return x;
}
struct graph{
int cnt,head[MAXN],vis[MAXN];
LL dis[MAXN];
struct Edge
{
int next,to;LL dis;
}edge[2*MAXN];
queue<int> q;
void addedge(int from,int to,LL dis)
{
edge[++cnt].next=head[from];
edge[cnt].to=to;
edge[cnt].dis=dis;
head[from]=cnt;
}
void spfa(int s)
{
for(int i=1;i<=N;i++)
dis[i]=INF;
memset(vis,0,sizeof(vis));
q.push(s); vis[s]=1; dis[s]=0;
while(!q.empty())
{
int x=q.front(); q.pop();
vis[x]=0;
for(int i=head[x];i;i=edge[i].next)
{
int to=edge[i].to;
if(dis[to]>dis[x]+edge[i].dis)
{
dis[to]=dis[x]+edge[i].dis;
if(!vis[to]) q.push(to),vis[to]=1;
}
}
}
}
}g1,g2;
int main()
{
scanf("%d %d %d %d",&N,&M,&S,&T);
for(int i=1;i<=M;i++)
{
scanf("%d %d %lld %lld",&u,&v,&a,&b);
g1.addedge(u,v,a); g1.addedge(v,u,a);
g2.addedge(v,u,b); g2.addedge(u,v,b);
}
g1.spfa(S); g2.spfa(T);
ans[N+1]=INF;
for(int i=N;i>=1;i--) ans[i]=Min(ans[i+1],g1.dis[i]+g2.dis[i]);
for(int i=1;i<=N;i++)
printf("%lld\n",f-ans[i]);
return 0;
}
P.S:终于知道了咋在结构体里面搞函数,跟在外面其实是一样的写法。