【环形线性区间dp】[CF]E. Petya and Post

【环形线性区间dp】[CF]E. Petya and Post

【环形线性区间dp】[CF]E. Petya and Post

题意(稍微变型):有n个加油站,编号为1到n,且每个加油站都能提供\(a_i\)的油量(只能提供一次),并且从一个加油站到相邻的加油站会消耗一定量的油量。求在这n个加油站中有几个加油站可以满足顺时针或者逆时针地整整跑完一圈?

思路:如果中途一旦出现油量小于0的情况(假设真的能)就说明车子停到一半,就没有继续跑了。

dp[i]表示从i点出发且最终又回到终点这样的路线中间出现了的最小值(这是一个非常重要且起决定作用的因素)

先单单从顺时针方向进行考虑,从点i+1出发和从点i出现的不同之处在于从i+1点出发的所有过程脱去先从i点取到油并且耗费一定油最终剩余或者亏欠的油量的影响,而,而脱去某种影响意思就是要去做一下修正操作,而这个修正操作也会直接影响到中途出现的最小值(最危险的情况),如果是

\(D_1=a_1-b_1\), (第i个点出发到i+1点的且没在i+1点加油的情况)

\(D_2=(a_1+a_2)-(b_1+b_2)\), (第i个点出发到i+2点的且没在i+2点加油的情况)

\(D_3=(a_1+a_2+a_3)-(b_1+b_2+b_3)\),

\(Dn=(a_1+a_2+…+a_n)-(b_1+b_2+…+b_n)\)

令\(D_{min}=min\{D_i\}\)

\(E_1=a_2-b_2=D_2-(a_1-b_1)\),

\(E_2=(a_2+a_3)-(b_2+b_3)=D_3-(a_1-b_1)\),

\(E_3=(a_2+a_3+a_4)-(b_2+b_3+b_4)=D_4-(a_1-b_1)\),

\(E_{n-1}=(a_2+…+a_n)-(b_2+…+b_n)=D_n-(a_1-b_1)\)

\(En=(a_2+…+a_n+a_1)-(b_2+…+b_n+b_1)\)

可以建立坐标系,将E和D的函数绘制出来,发现E的图像基本上是D的图像左移一个单位并向下平移\((a_1-b_1)\)绘制出来,也就是说最小值的位置是没有发生变化的

令\(E_{min}=min\{E_i\}=D_{min}-(a_1-b_1)\)

\(F_1=E_2-(a_2-b_2)\)

...

\(F_{n-1}=(a_3+…+a_n+a_1)-(b_3+…+b_n+b_1)=E_n-(a_2-b_2)\)

同理,可以建立坐标系,将E和D的函数绘制出来,发现E的图像基本上是D的图像左移一个单位并向下平移\((a_2-b_2)\)绘制出来,也就是说最小值的位置是没有发生变化的

#include <bits/stdc++.h>
#define MEM(a,x) memset(a,x,sizeof(a))
#define W(a) while(a)
#define gcd(a,b) __gcd(a,b)
#define pi acos(-1.0)
#define PII pair<int,int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define MAX 1000005
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define lowbit(x) (x&-x)
using namespace std;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9')
        x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int N = 1E5+5;
int n,m,dp[N];
bool ans[N];
int main()
{
	n=read();
	int A[n+1],B[n+1];
	for(int i=1;i<=n;i++)
        A[i] = read();
    for(int i=1;i<=n;i++)
        B[i] = read();
    int tmp=0;
    for(int i=1;i<=n;i++)
    {
    	tmp+=A[i]-B[i];
        dp[1]=min(dp[1],tmp);
	}
	if(dp[1]>=0) ans[1]=1;
	for(int i=2;i<=n;i++)
	{
		dp[i]=dp[i-1]-(A[i-1]-B[i-1]);
	    if(dp[i]>=0) ans[i]=1;
	}    
	//逆时针相当于把第n个点倒一下当成起点 
	tmp=0,B[0]=B[n];
	memset(dp,0,sizeof(dp));
    for(int i=n;i>=1;i--)
    {
    	tmp+=A[i]-B[i-1];
        dp[n]=min(dp[n],tmp);
	}
	if(dp[n]>=0) ans[n]=1;
	for(int i=n-1;i>=1;i--)
	{
		dp[i]=dp[i+1]-(A[i+1]-B[i]);
	    if(dp[i]>=0) ans[i]=1;
	} 
	vector<int> vec;   
	for(int i=1;i<=n;i++)
	    if(ans[i]) vec.push_back(i);
    sort(vec.begin(),vec.end());
    cout<<vec.size()<<endl;
    for(int i=0;i<vec.size();i++)
    {
    	if(i!=0) cout<<" ";
        cout<<vec[i];
	}
    return 0;
}

上一篇:如何在Android中捕获音频的输出流?


下一篇:CF1042D Petya and Array