CF1743 Educational Codeforces Round 137 (Rated for Div. 2)

A. Password

两个数 \(A\)\(B\) 的排列方法有 \(6\) 种,所以
答案就是 \(C^2_{10-n} \times 6\)
顺带复习一下组合数的计算方法

点击查看代码
#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long LL;
int a[200];
int c[30][30];
int calc()
{
	for(int i=0;i<=10;i++)
	{
		for(int j=0;j<=i;j++)
		{
			if(!j)c[i][j]=1;
			else c[i][j]=(c[i-1][j]+c[i-1][j-1]);
		}
		
	}
	return 0;
}
int main()
{
	int T;
	scanf("%d",&T);
	calc();
	while(T--)
	{
		//freopen("uva.txt","r",stdin);
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
		}
		
		printf("%d\n",c[10-n][2]*6);	
	}
	
	return 0;
}

B. Permutation Value

构造题,只要保证 \(1\) 与其他的数构不成排列.
\(1\) 放最左边 , \(2\) 放最右边,其他位置随便放 即可.

C. Save the Magazines

这道题一开始写假了,他说能把报纸的盖子从第 \(i\) 个箱子移动到第 \(i-1\) 个箱子,但是没说能一直往前移,只能移动一次!疯狂 \(WA\) (下次再也不乱交了呜呜呜,排名咔咔掉)
如果能一直往前移的话,方法就是优先队列.代码如下

点击查看代码
#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long LL;
priority_queue<int,vector<int> ,less<int> >q;

int main()
{
	//freopen("uva.txt","r",stdin);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		while(q.size())q.pop();
		
		int n;
		scanf("%d",&n);
		string s;
		cin>>s;
		LL ans=0;
		for(int i=0;i<n;i++)
		{
			int x;
			scanf("%d",&x);
			q.push(x);
			if(s[i]=='1')
			{
				ans+=q.top();
				q.pop();
			}
		}
		printf("%lld\n",ans);	
	}
		return 0;
}

最多只能移动一格.但是也需要注意不能单纯比较这一格和之前一格的大小.
比如
\(\ 0\ 1\ 1\)
\(\ 4\ 5\ 1\)
在第二个位置,也就是 \(1\) 的时候发现 \(5\)\(4\) 大,选 \(5\) .然后到第三个位置,由于 \(5\) 已经被盖着了,所以只能选 \(1\) .得到结果是 \(6\) ,但这是错误的,正确答案应该是 \(9\) (盖 \(4\)\(5\) ).所以单纯贪心当前位置和前一位置是不行的,太短视,没有大局观.
所以想到用动态规划来解
\(dp[i][0]\) 表示第 \(i\) 个位置的盖子盖到前一个位置时所能盖住报纸的最大值
\(dp[i][1]\) 表示第 \(i\) 个位置的盖子盖到当前位置的报纸所能盖住报纸的最大值
那么
用第 \(i\) 位置的盖子盖住第 \(i\) 个盖子所能得到的最大值等于第 \(i-1\) 位置盖上或不盖的最大值加上第 \(i\) 个位置里的报纸
用第 \(i\) 位置的盖子盖住第 \(i-1\) 个盖子所能得到的最大值等于第 \(i-1\) 位置的盖子盖到前一个位置(或者干脆没有盖子)的最大值加上第 \(i-1\) 位置里的报纸
(注意如果第 \(i\) 位置没有盖子,\(dp[i][0]\)\(dp[i][1]\) 就等于 \(max(dp[i-1][0],dp[i-1][1])\) )

得到状态转移方程
\(s[i]==0\)
\(dp[i][0]=dp[i][1]=max(dp[i-1][0],dp[i-1][1])\)
\(s[i]=='1'\)
\(dp[i][0]=dp[i-1][0]+a[i-1]\)
\(dp[i][1]=max(dp[i-1][0],dp[i-1][1])+a[i]\)
蒟蒻又写出来一道动归题,泪目.

点击查看代码
#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<string.h>
#include<algorithm>
#define int long long
using namespace std;
typedef long long LL;
const int N=2e5+10;
int a[N];
int dp[N][2];
signed main()
{
	//freopen("uva.txt","r",stdin);
	int T;
	scanf("%lld",&T);
	while(T--)
	{
		int n;
		scanf("%lld",&n);
		string s;
		cin>>s;
		for(int i=0;i<n;i++)
		{
			scanf("%lld",&a[i]);
		}
		dp[0][1]=s[0]=='1'?a[0]:0;
		dp[0][0]=0;
		for(int i=1;i<n;i++)
		{
			if(s[i]=='0')
			{
				dp[i][1]=dp[i][0]=max(dp[i-1][0],dp[i-1][1]);
			}
			else
			{
				dp[i][1]=max(dp[i-1][1],dp[i-1][0])+a[i];
				dp[i][0]=dp[i-1][0]+a[i-1];
			}
		}
		
		printf("%lld\n",max(dp[n-1][0],dp[n-1][1]));	
	}
	
	return 0;
}

D. Problem with Random Tests

这道题我觉得很傻逼..

给你一个\(01\)串,问你从这个串中按任意方法找两个连续子串(可相交,可重合,随便你,只要是连续子串).他们转化成十进制数后进行或运算的最大值是多少.
这个串开头的\(0\)是没有意义的,应该去掉,所以现在串的开头是\(1\)
s1和s2也应该以1开头,前面的0需要舍弃(因为要转化成具体的数)
第一个串直接取这个串本身.毕竟,或运算,当然是越长越好.管他\(0\)还是\(1\) .
第二个串就尽量让s2中能有\(1\)与第一个串\(0\)的地方重合,而且是越在前面的 \(0\) 重合的优先级越高.
首先假定我们在s的中间取了一段子串作为s2,那么s2一直延伸到最后是最好的,即使后面有多出来的不会影响结果值(匹配时以s1为主串,s2比s1多出来的直接忽视掉.).
此时s1和s2有相同的末段,前面只是s2比s1短一截
那么第二个串s2的长度一定要包含到s1的第一个0前面那个1(不然就太短了)
并且我们注意到第二个串的前端也是越长越好,因为:
11010 一定比1010要好
11110 一定比1110要好.
因为1越多,s2的活动范围更大.
s2包含了s1第一个0前面那个1,s2又要越长越好,自然s2可以直接取s1了(s1第一个0前面全是1)
因此,s2直接取s
然后s2从s1中的第一个1开始进行或运算.
再从第二个1开始或运算
...
到s1中的最后1个1开始或运算
然后s1中的第一个0开始或运算.运算结束后.看看以上结果得到的最大值即可.

代码献上

点击查看代码
#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long LL;
int main()
{
	//freopen("uva.txt","r",stdin);
	int T=1;
	while(T--)
	{
		int n;
		scanf("%d",&n);
		string s;
		cin>>s;
		int id=-1;
		for(int i=0;i<s.size();i++)
		{
			if(s[i]=='1')
			{
				id=i;
				break;
			}
		}
		if(id==-1)
		{
			printf("0\n");
			continue;
		}
		string t=s;
		string ans=s;
		string temp=s;
		int front=id;
		for(int i=id;i<s.size();i++)
		{
			temp=s;
			id=front;
			for(int j=i;j<s.size();j++)
			{
				temp[j]=(s[j]-'0')|(t[id]-'0')+'0';
				id++; 
			}
			
			ans=max(ans,temp);
			if(s[i]=='0') break;
		}
		int f=0;
		for(int i=0;i<s.size();i++)
		{
			if(ans[i]=='0'&&f==0)
			{
				continue;
			}
			printf("%c",ans[i]);
			f=1;
		}
		printf("\n");
	}
	
	return 0;
}

E. FTL

为了不误导大家,大家直接参考下面大佬的,讲的真好!
https://zhuanlan.zhihu.com/p/574589610
有这么一个游戏:你有两个激光发射器,他们充能花费 \(t_i\ (i=1,2)\) 的时间(冷却),可以造成 \(p_i\) 点伤害.有一个 \(boss\) ,血量为 \(h\) ,他有一个防御值 \(s\) ,每次对 \(boss\) 造成的伤害都会被抵消掉 \(s\) 点.激光充能完毕后可以选择直接射也可以选择等待另一个激光一起发射.
假如现在两个激光都准备好了,如果他们单独发射,会造成 \((p_1-s)\) + \((p_2-s)\) 点伤害.而如果他们一起发射,会造成 \(p_1+p_2-s\) 点伤害.
现在问杀死boss所需的最少时间.

\(1\leq h\leq 5000,1\leq s<min(p_1,p_2)\)

The first claim: greedy won't work. Maybe there is a sufficiently smart greedy, we weren't able to come up with it. The second claim: bruteforce won't work. The funny thing is that it actually worked on the constraints up to 2000, but again, we couldn't code any sufficiently fast one for 5000.

Thus, let's try some dynamic programming. Since all the times are huge, we'd want to avoid having them as the states. What is small, however, is the durability of the enemy ship and the number of shots we have to make to destroy it.
注意到 \(h\) 的范围相对比较小.

生词:
\(subtract\ \ v.减;减去\)

posted @ 2022-10-18 15:40  LZH_03  阅读(72)  评论(0编辑  收藏  举报