Educational Codeforces Round 61 (Rated for Div. 2)(题解)

Educational Codeforces Round 61 (Rated for Div. 2)(题解)

A. Regular Bracket Sequence

题目大意

给出一些括号串"((","()",")(","))" ,cnt1,cnt2,cnt3,cnt4个,问用这些括号串能否组成括号之间相互匹配的大括号串

解题思路

只要“((”,"))“的数目相等,且在其都为0时不存在”)("即可

AC代码

#include
using namespace std;
#define int long long
int32_t main()
{
	int  a,b,c,d;
	cin>>a>>b>>c>>d;
	if(a==d)
	{
		if(a==0&&c!=0) cout<<0<<endl;
		else cout<<1<<endl;
	}
	else cout<<0<<endl;
}

B. Discounts

题目大意

给出n个商品,你需要将他们全部买下,现在你有一些优惠券,使用优惠券i可以让你任意选择 k i k_i ki个商品,并让其中最便宜的那个免费

解题思路

贪心总是选最贵的 k i k_i ki个即可

AC代码

#include
using namespace std;
#define int long long 
const int sz=3e5+5;
int arr[sz];
int32_t main()
{
	int n;
	cin>>n;
	long long ans=0;
	for(int i=1;i<=n;i++) cin>>arr[i],ans+=arr[i];
	sort(arr+1,arr+1+n);
	int m;
	cin>>m;
	int k;
	while(m--)
	{
		cin>>k;
		cout<<ans-arr[n-k+1]<<endl;
	}
}

C. Painting the Fence

题目大意

给出n个刷墙工,其可以刷 [ l i , r i ] [l_i,r_i] [li,ri]的区域,现在要求让n-2个刷墙工去刷墙,让其刷尽可能多

解题思路

即揭去两名刷墙工的工作后使得被刷的长度最大.记录被涂层数为1和2的权值前缀和.暴力选择两两刷墙工,重合部分减去两层的涂数,非重合减去一层的涂数.得出最终答案即可

AC代码

#include
using namespace std;
typedef pair<int,int> pii;
int l[5005],r[5005];
pii s[5005];
int vis[5005];
int op[2][5005];
int main()
{
	int n,q;
	scanf("%d%d",&n,&q);
	memset(vis,0,sizeof(vis));
	memset(op,0,sizeof(op));
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d",&l[i],&r[i]);
		s[i].first=r[i],s[i].second=l[i];
		for(int j=l[i];j<=r[i];j++)
		vis[j]++;
	}
	sort(s+1,s+1+q);
	for(int i=1;i<=q;i++) r[i]=s[i].first,l[i]=s[i].second;
	for(int i=1;i<=n;i++)
	{
		op[0][i]=op[0][i-1],op[1][i]=op[1][i-1];
		if(vis[i]==1) op[0][i]++;
		if(vis[i]==2) op[1][i]++;
	}
	int ans=0;
	for(int i=1;i<=n;i++) if(vis[i]) ans++;
	int cans=ans;
	for(int i=1;i<=q;i++)
	{
		for(int j=i+1;j<=q;j++)
		{
			if(l[j]>r[i]) cans=min(cans,(op[0][r[i]]-op[0][l[i]-1])+(op[0][r[j]]-op[0][l[j]-1]));
			else if(l[j]<=r[i]&&l[j]>=l[i]) cans=min(cans,(op[0][r[j]]-op[0][l[i]-1])+(op[1][r[i]]-op[1][l[j]-1]));
			else cans=min(cans,(op[0][r[j]]-op[0][l[j]-1])+(op[1][r[i]]-op[1][l[i]-1]));
		}
	}
	cout<<ans-cans<<endl;
}

D. Stressful Training

题目大意

有n台笔记本电脑其拥有初始电量 a i a_i ai且每单位时间减少 b i b_i bi的电量,现可以通过充电器对一些电脑进行充电,其每个单位时间只能给一台电脑冲x的电,要求所有的电脑在k的时间内不关机.问x的最小值为多少

解题思路

二分搜索x,对每台电脑算出其第一次电量用尽的时间为那个时间增加一个充电标记,并记录充电次数.如充电次数>k则此x不可行,如一个时间的充电次数大于其时间则也不可行

AC代码

#include
using namespace std;
const int sz=2e5+5;
int cnt[sz];
int n,k;
#define int long long
int a[sz],b[sz];
bool check(int x)
{
	memset(cnt,0,sizeof(cnt));
	int tot=k-1;
	for(int i=1;i<=n;i++)
	{
		if(!b[i]) continue;
		int sum=a[i];
		while(sum<(k-1)*b[i])
		{
			if(sum/b[i]+1<=k) cnt[sum/b[i]+1]++;
			if(!tot) return false;
			tot--;
			sum+=x;
		}
	}
	for(int i=1;i<=k;i++)
	{
		cnt[i]=cnt[i]+cnt[i-1];
		if(cnt[i]>i) return false;
	}
	return true;
}
int32_t main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
	int l=0,r=1e18;
	int ans=-1;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(check(mid))
		{
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	printf("%lld\n",ans);
}	

E. Knapsack

题目大意

现有1,2,3…8数字各 c n t 1 , c n t 2 , c n t 3 , . . . c n t 8 cnt_1,cnt_2,cnt_3,...cnt_8 cnt1,cnt2,cnt3,...cnt8个,给出一个w现要求求出一个小于等于w的尽可能大的数字和

解题思路

已知1-8所有数的公倍数为840.每个数具有 c n t [ i ] cnt[i] cnt[i]个,那么我们不妨将 c n t [ i ] ∗ i cnt[i]*i cnt[i]i写作 k i ∗ 840 + q i k_i*840+q_i ki840+qi对于一个最优的答案我们,我们也同样可以写作 k ∗ 840 + q k*840+q k840+q为此我们只需要求出所有可能的q,并求出相应的最大k即可.为此我们对这个数量进行动态规划,将前i个数,其 q i q_i qi的和为j时所能达到的k最大记为 d p [ i ] [ j ] dp[i][j] dp[i][j]则我们可以通过递推关系式
d p [ i + 1 ] [ j + q ∗ ( i + 1 ) ] = m a x ( d p [ i + 1 ] [ j + q ∗ ( i + 1 ) ] , d p [ i ] [ j ] + ( c n t [ i + 1 ] − q ) / ( l c m / ( i + 1 ) ) ) ; dp[i+1][j+q*(i+1)]=max(dp[i+1][j+q*(i+1)],dp[i][j]+(cnt[i+1]-q)/(lcm/(i+1))); dp[i+1][j+q(i+1)]=max(dp[i+1][j+q(i+1)],dp[i][j]+(cnt[i+1]q)/(lcm/(i+1)));
来得到下一个状态的k最大值,最后再遍历i为8时所有可能的j,找出其中最大值即可.

AC代码

#include
#define int long long 
using namespace std;
const int lcm=840;
typedef long long LL;
int cnt[9];
LL dp[9][8*lcm];
int32_t main()
{
	LL w;
	cin>>w;
	for(int i=1;i<=8;i++) cin>>cnt[i];
	memset(dp,-1,sizeof(dp));
	dp[0][0]=0;
	for(int i=0;i<8;i++)
	for(int j=0;j<8*lcm;j++)
	{
		if(dp[i][j]==-1) continue;
		int qmax=min(840/(i+1),cnt[i+1]);
		for(int q=0;q<=qmax;q++)
		{
			dp[i+1][j+q*(i+1)]=max(dp[i+1][j+q*(i+1)],dp[i][j]+(cnt[i+1]-q)/(lcm/(i+1)));
		}
	}
	LL ans=0;
	for(int i=0;i<8*lcm;i++)
	{
		if(dp[8][i]!=-1&&i<=w)
		ans=max(ans,i+lcm*(min(dp[8][i],(w-i)/lcm)));
	}
	cout<<ans<<endl;
}

F. Clear the String

题目大意

给出一段字符串,每次可以消去其中的一段字母完全相同的字串,问最少需要多少次让整个串消完

解题思路

区间DP即可.对于每个长度len如果两端相同则其相当于两端可以少消一次.且如果有段中两个字符相同则段中间那个相同的可以免费消除一次

AC代码

#include
using namespace std;
char s[505];
int dp[505][505];
int main()
{
	int n;
	scanf("%d",&n);
	scanf("%s",s+1);
	for(int i=1;i<=n;i++) dp[i][i]=1;
	for(int i=1;i<=n-1;i++)
	{
		if(s[i]==s[i+1]) dp[i][i+1]=1;
		else dp[i][i+1]=2;
	}
	int j;
	for(int len=3;len<=n;len++)
	{
		for(int i=1;i+len-1<=n;i++)
		{
			j=i+len-1;
			dp[i][j]=len;
			dp[i][j]=min(dp[i][j],dp[i+1][j]+1);
			if(s[i]==s[i+1]) dp[i][j]=min(dp[i][j],dp[i+1][j]);
			if(s[i]==s[j]) dp[i][j]=min({dp[i][j],dp[i][j-1],dp[i+1][j]});
			for(int k=i+2;k<j;k++)
			{
				if(s[i]==s[k]) dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]);
			}
		}
	}
	printf("%d\n",dp[1][n]);
}

你可能感兴趣的:(codeforce,套题题解)