CF:1607D. Blue-Red Permutation(巧妙)、1607E. Robot on the Board 1(思维);双人作业问题(dp)

1607D. Blue-Red Permutation

题意:

给定长度为 n 的数列,每个位置的颜色为蓝或者红。
判断该数列能否变换成n的全排列,通过任意次下述操作:
任选红色位置的数+1,或任选蓝色位置的数-1。

思路:

先将所给的数看作位置,所给的颜色放到对应到这个位置上。
对于超过1~n范围的数,如果是红色并且小于1,那么就放在1位置上;如果是蓝色并且大于n,那么就放在n位置上。其他的不能利用,舍弃。

然后就是要将颜色移动(把颜色看作物品,移动到一个位置上),看能否让每一个位置至少有一种颜色。

因为蓝色只能往左移动,红色只能往右移动,所以可以先将蓝色都往左移,再将红色都往右移,判断是否能将所有位置补全。

定义cnt为前面已经染色的个数。
遍历所有位置,如果当前位置有蓝色,那么cnt加上蓝色个数。但是不能超过当前的位置i;
遍历所有位置,如果当前位置有红色,那么cnt可以往后加。但是当前的cnt一定不小于i:因为蓝颜色都已经用完了,只剩下能往右移动的红颜色了。所以如果前面还有位置没补齐的话,就是无解了。

整体思路:
先将蓝颜色都往左移,再将红颜色右移。
(因为蓝颜色只能往左移,红颜色只能往右移,所以要尽量用蓝颜色使前面的补全,再用红颜色补剩下的)

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];
int B[N],R[N];

int main(){
    
    
	cin>>T;
	while(T--)
	{
    
    
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i],B[i]=R[i]=0;
		for(int i=1;i<=n;i++)
		{
    
    
			char c;cin>>c;
			if(c=='B'&&a[i]>n) a[i]=n;
			if(c=='R'&&a[i]<1) a[i]=1;
			
			if(c=='B'&&a[i]>=1&&a[i]<=n) B[a[i]]++;
			if(c=='R'&&a[i]>=1&&a[i]<=n) R[a[i]]++;
		}
		
		int cnt=0,flag=0;
		for(int i=1;i<=n;i++){
    
    
			cnt+=B[i];
			cnt=min(cnt,i); //注意前面颜色的个数不能超过当前位置i
		}
		for(int i=1;i<=n;i++){
    
    
			if(cnt>=i-1) cnt+=R[i];
			if(cnt<i) flag=1; //未能衔接上,不满足
		} 
		
		if(flag) cout<<"No\n";
		else cout<<"Yes\n";
	}
	
	return 0;
}

1607E. Robot on the Board 1(思维)

题意:

一个机器人在一个n*m的棋盘上按照字符串s每个字符所代表的指令移动。如果掉出棋盘,则结束。
挑选一个起点,使得机器人能够进行的指令数最多。

思路:

因为指令为一行,一个一个执行,所以最多会在其走过的横向距离大于m 或 纵向距离大于n 时结束。
所以可以遍历求出这个指令。
求出由原点最多往左走的位置left,最多往上走的位置front。

那么最佳起点便为(-left+1, -front+1)。 搞个偏移量。

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];

int main(){
    
    
	cin>>T;
	while(T--)
	{
    
    
		cin>>n>>m;
		string s;cin>>s;
		
		int front=0,end=0,pos=0;
		for(int i=0;i<s.size();i++)
		{
    
    
			if(end-front+1>=n) break;
			if(s[i]=='U') pos--;
			if(s[i]=='D') pos++;
			front=min(front,pos);
			end=max(end,pos);
		}
		cout<<-front+1<<" ";
		
		int l=0,r=0;pos=0;
		for(int i=0;i<s.size();i++)
		{
    
    
			if(r-l+1>=m) break;
			if(s[i]=='L') pos--;
			if(s[i]=='R') pos++;
			l=min(l,pos);
			r=max(r,pos);
		}
		cout<<-l+1<<endl;
	}
	
	return 0;
}

经验:

坐标题:横纵坐标分别搞。


附上一个今天上机测试的一道动规题:

题意:

两个长度为 n 的数组 a,b,对于每个位置 i,可以选择 ai 或者 bi。选择的 ai 之和为 sum1,bi 之和为 sum2。
问,max(sum1,sum2) 最小为多少?

思路:

状态表示f[i,j],第一个同学做前 i 个任务所用的时间为 j 时,第二个同学所用的最少时间。
初始化f[0,0]=0,做前 0 个任务所用的时间为 0 时,第二个同学所用的最小时间为 0。

  • 第i个任务交给第一个同学: 第二个同学不做,那么其所用的时间就为1同学做上次任务时所用的时间: f[i,j]=min(f[i,j],f[i-1,j-a[i]])

  • 第i个任务交给第二个同学: 那么第二个同学所用的时间就为上一任务所用的时间加上当前任务所用的时间:f[i,j]=min(f[i,j], f[i-1,j] + b[i])

最后遍历求出max(i, f[n,i])中的最小值。

Code:

int main(){
    
    
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],m+=a[i];
	for(int i=1;i<=n;i++) cin>>b[i];
	
	memset(f,0x3f,sizeof f);
	f[0][0]=0;
	
	for(int i=1;i<=n;i++)
	{
    
    
		for(int j=0;j<=m;j++)
		{
    
    
			if(j>=a[i]) f[i][j]=min(f[i][j],f[i-1][j-a[i]]);
			f[i][j]=min(f[i][j],f[i-1][j]+b[i]);
		}
	}
	int ans=1e9;
	for(int i=1;i<=m;i++){
    
    
		ans=min(ans,max(i,f[n][i]));
	} 
	cout<<ans;
	
	return 0;
} 

经验:

状态表示很特别,用第一个的状态来表示出第二个的最优状态,最后取两种状态的最值。

猜你喜欢

转载自blog.csdn.net/Mr_dimple/article/details/121131476