社区讨论

在这里你可以轻松地获得一个AC数

P13501 「Cfz Round 6」Imaichi参与者 1已保存回复 0

讨论操作

快速查看讨论及其快照的属性,并进行相关操作。

当前回复
0 条
当前快照
1 份
快照标识符
@mdlmb38z
此快照首次捕获于
2025/07/27 19:50
7 个月前
此快照最后确认于
2025/07/27 19:50
7 个月前
查看原帖
TLE 70pts 但是时间复杂度正确,为 O(nmT)O(nmT) ,大样例跑了 3.61s 求助卡常
关于标题
记得帮助别人卡常的AC这道题不算作弊
代码CPP
#include <bits/stdc++.h>
//...
//真的对吗?
using namespace std;
int n,m,s,k; 
int a[1023][1023];
int e[1023][1023],dp[1023][1023];//进入时在结算后最多还剩多少钱,将要退出时最多还剩多少钱 
constexpr int inf=0x3f3f3f3f;
int p[1023],q[1023];//去的时候至少要多少钱和回来的时候最多还剩多少钱 
//p是说在结算这个格子之前至少要有多少钱 
bool flag1[1023],flag2[1023],flag[1023];//从可以开始的点出发,有哪些点是可以到达的 
int mian()
{
	cin>>n>>m>>s>>k;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			e[i][j]=dp[i][j]=-inf;
	for(int i=0;i<=m+1;i++)
		p[i]=inf,q[i]=-inf;
	p[0]=0,p[m+1]=0;
	for(int i=1;i<=m;i++)
	{
		int p=k<s+a[1][i]?k:s+a[1][i];
		if(p>=0)
			e[1][i]=p;
	}
	for(int i=1;i<n;i++)
	{
		for(int j=1;j<=m;j++)
			dp[i][j]=e[i][j];
		memset(flag1,0,sizeof(flag1));
		memset(flag2,0,sizeof(flag2));
		for(int j=m;j>=1;j--)
		{
			if(e[i][j]>=0)
			{
				int cur=j,remain=e[i][j];
				while(cur<=n&&remain>=0)
				{
					flag1[cur]=true;
					if(cur!=j)
						remain=min(remain+a[i][cur],k);
					cur++;
				}
			}
		}
		for(int j=1;j<=m;j++)
		{
			if(e[i][j]>=0)
			{
				int cur=j,remain=e[i][j];
				while(cur>=1&&remain>=0)
				{
					flag2[cur]=true;
					if(cur!=j)
						remain=min(remain+a[i][cur],k);
					cur--;
				}
			}
		}
		for(int j=1;j<=m;j++)
			flag[j]=flag1[j]|flag2[j];
		//考虑向左走找到刷钱点
		int last=-inf;
		for(int j=1;j<=m;j++)
			p[j]=inf,q[j]=-inf;
		for(int j=1;j<=m;j++)
		{
			//判断这是不是刷钱点
			//刷钱点是两个正数或者一正另一个<=0,应该算从最近的正数出发或者到最近的正数
			//我们认为刷钱点在j,当且仅当a_{j-1}与a_j构成了一个刷钱点,且a_j>0
			//其实a_j在左边也可以 
			//而且还要可以走到 
			if(flag[j]&&((j!=1&&a[i][j-1]+a[i][j]>0&&a[i][j]>=0)||(j!=m&&a[i][j]+a[i][j+1]>0&&a[i][j]>=0)))
			{
				//发现刷钱点
				p[j]=0,q[j]=k;
				last=j;
				continue; 
			}
			if(last==-inf)
				continue;
			//我们需要维护从上一个刷钱点到j还剩多少钱
			//还有从j走入上一个刷钱点在开始时至少要多少钱
			//第一点显然
			q[j]=min(q[j-1]+a[i][j],k);
			if(q[j]<0)//无法抵达j 
				q[j]=-inf;
			p[j]=max(p[j-1]-a[i][j],0);
			if(p[j]>k)
				p[j]=k;
		}
		for(int j=1;j<=m;j++)
			if(p[j-1]<=e[i][j])
				dp[i][j]=max(dp[i][j],q[j]);
		//向右走到刷钱点同理 
		last=inf;
		for(int j=1;j<=m;j++)
			p[j]=inf,q[j]=-inf;
		for(int j=m;j>=1;j--)
		{
			if(flag[j]&&((j!=m&&a[i][j+1]+a[i][j]>0&&a[i][j]>=0)||(j!=1&&a[i][j]+a[i][j-1]>0&&a[i][j]>=0)))
			{
				p[j]=0,q[j]=k;
				last=j;
				continue;
			}
			if(last==inf)
				continue;
			q[j]=min(q[j+1]+a[i][j],k);
			if(q[j]<0)
				q[j]=-inf;
			p[j]=max(p[j+1]-a[i][j],0);
			if(p[j]>k)
				p[j]=k;
		}
		for(int j=1;j<=m;j++)
			if(p[j+1]<=e[i][j])
				dp[i][j]=max(dp[i][j],q[j]);
		//但是我们可以从一个格子走到其他地方,需要考虑走到其他地方还剩多少
		//对于每两个刷钱点之间的格子,一定从刷钱点出发最优
		int remain=-inf;
		for(int j=1;j<=m;j++)//往右走 
		{
			if((remain>=0||flag[j])&&((j!=1&&a[i][j-1]+a[i][j]>0&&a[i][j]>=0)||(j!=m&&a[i][j]+a[i][j+1]>0&&a[i][j]>=0)))
			{
				remain=k;
				dp[i][j]=max(dp[i][j],remain);
				continue;
			}
			remain=min(remain+a[i][j],k);
			remain=max(dp[i][j],remain);
			if(remain<0)
				remain=-inf;
			dp[i][j]=max(dp[i][j],remain);
		}
		remain=-inf;
		for(int j=m;j>=1;j--)//往右走 
		{
			if((remain>=0||flag[j])&&((j!=1&&a[i][j-1]+a[i][j]>0&&a[i][j]>=0)||(j!=m&&a[i][j]+a[i][j+1]>0&&a[i][j]>=0)))
			{
				remain=k;
				dp[i][j]=max(dp[i][j],remain);
				continue; 
			}
			remain=min(remain+a[i][j],k);
			remain=max(dp[i][j],remain);
			if(remain<0)
				remain=-inf;
			dp[i][j]=max(dp[i][j],remain);
		}
		for(int j=1;j<=m;j++)
		{
			int next=min(k,dp[i][j]+a[i+1][j]);
			if(next>=0)
				e[i+1][j]=next;
		}
	}
	int ans=-inf;
	for(int i=1;i<=m;i++)
		if(e[n][i]>ans)
			ans=e[n][i];
	if(ans==-inf)
		cout<<"-1\n";
	else
		cout<<ans<<"\n";
	return 0;//完结撒花 
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int ID,t;
	cin>>ID>>t; 
	for(int i=1;i<=t;i++)
		mian();
	return 0;
}

回复

0 条回复,欢迎继续交流。

正在加载回复...