CF1400:1463B(构造,思维)、1303B(二分答案)、977D(map,暴搜)

1463.B. Find The Array(构造,思维)

题意:

给出数列a,其总和为S。
要构造一个数列b,满足其相邻两项可以相互整除,并且|ai-bi|之和不超过S/2

思路:

两种思路。
思路1:
设a数列的奇数位置之和为S奇,偶数位置之和为S偶。那么,S奇+S偶=S。
同时,满足S奇和S偶中,至少有一个不超过S/2。(反证,如果都超过了,那么两者之和就超过S了)
我们把数列b中,这种位置 (奇数或偶数位置)上的所有数构造为1,其他位置构造为ai
首先,满足了相邻位置两项可以互相整除。
其次,这种位置(奇/偶 位置)的|ai-bi|之和不超过S/2,另一种位置的|ai-bi|之和为0。总和不超过S/2。

思路2:
将b数列的每个数bi,构造为不超过对应位置ai的,2的最高次幂数
这样,每个位置的|ai-bi|的值不超过ai的一半,那么所有位置|ai-bi|的总和也就不超过S/2。

Code:

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

//思路2:
int main(){
    
    
	Ios;
	
	cin>>T;
	while(T--)
	{
    
    
		cin>>n;
		for(int i=1;i<=n;i++)
		{
    
    
			int x;cin>>x;
			int p=log(x)/log(2);
			cout<<(ll)pow(2,p)<<" ";
		}
		cout<<endl;
	}
	
	return 0;
}

//思路1:
int main(){
    
    
	Ios;
	
	cin>>T;
	while(T--)
	{
    
    
		cin>>n;
		
		ll sum1=0,sum2=0,sum=0;
		for(int i=1;i<=n;i++)
		{
    
    
			cin>>a[i];
			if(i%2) sum1+=a[i];
			else sum2+=a[i];
			sum+=a[i];
		}
		
		if(sum2<=sum/2)
		{
    
    
			for(int i=1;i<=n;i++)
			{
    
    
				if(i%2) cout<<a[i]<<" ";
				else cout<<1<<" ";
			}
		}
		else
		{
    
    
			for(int i=1;i<=n;i++)
			{
    
    
				if(i%2) cout<<1<<" ";
				else  cout<<a[i]<<" ";
			}
		}
		cout<<endl;
	}
	
	return 0;
}

经验:

以后碰见互相整除问题要向2的幂次数上想。对于任意两个2的幂次数,都是可以相互整除的。
1和其他任意数也是可以的。

另外就是:S奇和S偶,至少有一个是不超过S/2的。


1303B. National Project(二分答案)

题意:

有一个长度为n公里的公路要施工,每天施工1公里。
有两种天气,好天气 x天 和 坏天气 y天,轮流出现。这n公里的施工要保证好天气的天数不少于一半。
问,施工完成最少需要多少时间?

思路:

对于最少时间不好求,但是对于给定的时间,可以判断其是否满足。
判断给定x天是否满足:当前x天中好天气的天数+能用的坏天气天数 是否超过n。
所以就可以二分答案。

能用的坏天气天数为:min(总共的坏天气天数,n/2)。

Code:

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

bool check(int mid)
{
    
    
	int q=mid/(x+y);
	int left=mid%(x+y);
	
	int t=q*x+min(left,x);
	int t1=min(n/2,q*y+max(0ll,left-x));
	
	return t+t1>=n;
}

signed main(){
    
    
	Ios;
	
	cin>>T;
	while(T--)
	{
    
    
		cin>>n>>x>>y;
		
		int l=n,r=1e18;
		while(l<r)
		{
    
    
			int mid=l+r>>1;
			
			if(check(mid)) r=mid;
			else l=mid+1;
		}
		cout<<l<<endl;
	}
	
	return 0;
}

经验:

原本以为二分答案已经掌握的很好了,但是见到这个题还是没想起来二分。。
以后要思维发散些,做不出来的时候想想这些算法能不能用。


977D. Divide by three, multiply by two(map+暴搜)

题意:

给出一个长度为n的数列a,输出满足下列要求的再排列。(n≤100)
要求:后一个数为前一个数除3 或 前一个数的2倍。

思路:

map记录下每个数是否存在。
暴搜即可。

Code:

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

void dfs(int u)
{
    
    
	if(u==n+1){
    
    
		for(int i=1;i<=n;i++) cout<<f[i]<<" ";
		return;
	}
	if(f[u-1]%3==0&&mp[f[u-1]/3]){
    
    
		f[u]=f[u-1]/3;
		dfs(u+1);
	}
	
	if(mp[f[u-1]*2]){
    
    
		f[u]=f[u-1]*2;
		dfs(u+1);
	}
}

int main(){
    
    
	Ios;
	
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]++;
	
	for(int i=1;i<=n;i++)
	{
    
    
		f[1]=a[i];
		dfs(2);
	}
	
	return 0;
}

挺好。

Guess you like

Origin blog.csdn.net/Mr_dimple/article/details/121002183