Codeforces Round684(Div 2)——解题报告

题目链接

https://codeforces.com/contest/1440

A题:Buy the String

题意: 

给你一个长度为n的01串,你可以执行任意次翻转操作(即把第i位翻转),每次操作消耗的代价为h,定义0的代价为c0,1的代价为c1,对于一个串的代价就是0和1的代价和,求最小代价和。

题解:

我们考虑每一位,如果当前位为0,考虑翻转和不翻转的情况,那么最小的代价就是min(c0,c1+h),同理可以得出当前位为1的最小代价为min(c1,c0+h).最终的代价和就是所有为的最小代价的和。

代码:

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<long long,long long>
#define pil pair<int,long long>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define debug puts("ac")
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
inline long long lread(){
	long long s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
const int N = 1e5+7;
int main(){
	int T=read();
	while(T--){
		int n=read(),c0=read(),c1=read(),h=read();
		string s;cin>>s;
		int ans=0;
		rp(i,0,n-1){
			if(c0>c1+h){
				ans+=c1+(s[i]=='0')*h;
			}
			else if(c1>c0+h){
				ans+=c0+(s[i]=='1')*h;
			}
			else{
				if(s[i]=='0') ans+=c0;
				else ans+=c1;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

B题Sum of Medians

题意

给你n*k个数,需要把这些数分成k个大小为n的数列,保证这n个数列的中位数的和最大,并输出最大和。

题解

首先肯定需要排序,然后就考虑排序后怎么构造了,可以手推几个n试试能不能找到规律。

 不难发现最终的开始位置是n*k-(n/2),长度是(n/2)+1,最终位置是((n-1)/2)*k+1.

答案就是这些位置的数字和(即中位数的和)。

代码

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define debug puts("ac")
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
inline ll lread(){
	ll s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
const int N = 1e6+7;
int a[N];
int main(){
	int T=read();
	while(T--){
		int n=read(),k=read();
		rp(i,1,n*k) a[i]=read();
		sort(a+1,a+1+n*k);
		ll sum=0;
		int cur=n*k-(n/2);
		while(cur>((n-1)/2)*k){
			sum+=a[cur];
			// cout<<cur<<" "<<endl;
			cur-=(n/2)+1;
		}
		cout<<sum<<endl;
	}
	return 0;
}

C1题+C2题Binary Table (Easy Version)+Binary Table (Hard Version)

题意

给你一个n*m的01矩阵,你可以进行一种操作,即在一个2*2的矩阵中选三个位置,把这三个位置的数进行翻转。现在让你求出把这n*m的01矩阵变成全0矩阵的方案,简单版本需要保证最终答案的大小小于3*n*m,困难版本则是n*m.

题解

首先考虑简单版本的做法,我们考虑2*2的矩阵按照0和1的个数可以分成5种情况。

假设4个0为⑤,4个1为④,1个0和3个1为③,1个1和3个0为②,2个1和2个0是①。

他们之间的转换就是    ④->②->①->③->⑤。

虽然④状态转换到⑤状态需要4步,但是因为他是4个1,最终会把4个1都变成0,所以最终答案会保证在3*n*m内实现翻转为全0矩阵。

下面考虑困难版本的做法,这就需要比较巧妙的构造了,我们可以从最后一行开始,不断地把最后一行为1的位置变成0(即选择上一行和它相邻的两个位置和当前位置),最终会留下第一行和第二行,这里需要m*(n-2)步。

然后从右到左再把每一列都消成0,直至剩下前两列,即每次选择当前列为1的位置和上一列相邻的两个位置翻转,这里需要2*(m-2)步。

这时就只剩下前两行和前两列,有简单版本我们知道,一个2*2的矩阵消成全0矩阵最多需要4步,因此总步数为m*(n-2)+2*(m-2)+4=n*m。

最后的消除直接根据简单版本的做法暴力消就行了。

这个题码量还是不小的,不过参考了jiangly的做法,发现了一个码量不大的很妙的写法,下面给出代码。

代码

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define debug puts("ac")
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
const int N = 1e6+7;
char s[107][107];
int a[107][107];
vector<tuple<int,int,int,int,int,int>> res;
int n,m;
void change(int x1,int y1,int x2,int y2,int x3,int y3){
	res.emplace_back(make_tuple(x1,y1,x2,y2,x3,y3));
	a[x1][y1]^=1;
	a[x2][y2]^=1;
	a[x3][y3]^=1;
}
int main(){
	int T=read();
	while(T--){
		res.clear();
		int n=read(),m=read();
		rp(i,0,n-1) scanf("%s",s[i]);
		rp(i,0,n-1) rp(j,0,m-1) a[i][j]=s[i][j]-'0';
		RP(i,n-1,2){
			rp(j,0,m-1){
				if(a[i][j]==0) continue;
				if(j>0) change(i,j,i-1,j-1,i-1,j);
				else change(i,j,i-1,j+1,i-1,j);
			}
		}
		RP(i,m-1,2){
			if(a[0][i]==1) change(0,i,0,i-1,1,i-1); 
			if(a[1][i]==1) change(1,i,0,i-1,1,i-1); 
		}
		if(a[1][1]==1) change(1,1,0,1,1,0);
		if(a[0][1]+a[1][0]+a[0][0]==3){
			change(0,1,1,0,0,0);
		}
		else if(a[0][1]+a[1][0]==2){
			change(0,0,1,1,0,1);
			change(0,0,1,1,1,0);
		}
		else if(a[0][1]+a[0][0]==2){
			change(1,0,1,1,0,1);
			change(1,0,1,1,0,0);
		}
		else if(a[0][0]+a[1][0]==2){
			change(0,1,1,1,0,0);
			change(0,1,1,1,1,0);
		}
		else if(a[0][0]==1){
			change(0,0,1,0,1,1);
			change(0,0,0,1,1,0);
			change(0,0,0,1,1,1);
		}
		else if(a[0][1]==1){
			change(0,1,0,0,1,1);
			change(0,1,1,0,0,0);
			change(0,1,1,0,1,1);
		}
		else if(a[1][0]==1){
			change(1,0,0,0,1,1);
			change(1,0,0,1,0,0);
			change(1,0,0,1,1,1);
		}
		rp(i,0,n-1) rp(j,0,m-1) assert(a[i][j]==0);
		cout<<res.size()<<endl;
		for(auto tp:res) cout<<get<0>(tp)+1<<" "<<get<1>(tp)+1<<" "<<get<2>(tp)+1<<" "<<get<3>(tp)+1<<" "<<get<4>(tp)+1<<" "<<get<5>(tp)+1<<endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>