Codeforces Beta Round #61 (Div. 2) E. Petya and Post

考虑最暴力的方法 ,就是从每点去模拟,用一个变量cur记录当前油箱的油量,一旦发现某个时刻cur小于0,则是无解。但这个做法一定是不可以了,会超时。

不过我们可以从暴力做法里面获得一些启发。考虑cur在变化过程中的最小值min_cur,明显有min_cur<=0,记dp[i]为从i点出发,想要转一圈回来,在i点未加油时最少需要的油量是多少。显然dp[i]=min_cur.

题目还有一个重要的信息,对a求和等于对b求和。那么在暴力求法中,cur最后一定归0(中途可能是负的),那么也就是说,在终点前一个点加油后油量一定是正的。那么dp[i]就有另一个含义从i点出发沿i,i+1,i+2,...,走到i-1所需的最小油量,因为能走到i-1,就一定能到i。

那么

dp[i]=max(b[i]-a[i],dp[i+1]-(a[i]-b[i]))=max(b[i]-a[i],dp[i+1]-a[i]+b[i])=dp[i+1]-a[i]+b[i] (dp[i+1]>=0)

b[i]-a[i]表示能从i到i+1

dp[i+1]-a[i]+b[i]表示先从i到i+1,在从i+1到i(dp[i+1]取第二个含义),合起来就是从i到i(dp[i]取第一个含义)

这样从任意一个点暴力求一次dp[i],再O(n)递推其他点的dp值即可。注意题目中有两个方向,那么做两次即可。

#include <bits/stdc++.h>
#define maxn 100009
using namespace std;
bool vis[maxn];
int a[maxn],b[maxn],dp[maxn],n;
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		scanf("%d",&a[i]);
	for(int i=0;i<n;i++)
		scanf("%d",&b[i]);
	int cur=0;
	for(int i=0;i<n;i++)
	{
		cur=cur+a[i]-b[i];
		dp[0]=min(dp[0],cur);
	}
	dp[0]=-dp[0];
	if(dp[0]==0)
		vis[0]=1;
	for(int i=n-1;i>0;i--)
	{
		dp[i]=dp[(i+1)%n]-(a[i]-b[i]);
		if(dp[i]==0)
			vis[i]=1;
	}
	memset(dp,0,sizeof(dp));
	cur=0;
	for(int i=n-1;i>=0;i--)
	{
		cur=cur+a[i]-b[((i-1)%n+n)%n];
		dp[n-1]=min(dp[n-1],cur);
	}
	dp[n-1]=-dp[n-1];
	if(dp[n-1]==0)
		vis[n-1]=1;
	for(int i=0;i<n-1;i++)
	{
		dp[i]=dp[((i-1)%n+n)%n]-(a[i]-b[((i-1)%n+n)%n]);
		if(dp[i]==0)
			vis[i]=1;
	}
	int cnt=0;
	for(int i=0;i<n;i++)
	{
		if(vis[i])
			cnt++;
	}	
	printf("%d\n",cnt);
	for(int i=0;i<n;i++)
	{
		if(vis[i])
		printf("%d ",i+1);
	}	
	system("pause");
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值