Codeforces Round #734 (Div. 3)

比赛链接

Codeforces Round #734 (Div. 3)

D1. Domino (easy version)

给你 \(t\) 组数据。对于每组数据给你一个 \(n \times m\) 的网格(\(n\) 为网格高度, \(m\) 为网格宽度,且网格的数量为偶数),要求在网格中放置多米诺骨牌,每个骨牌占据 \(1 \times 2\) 的网格区域。对于这 \(\frac{n m}{2}\) 个骨牌,要求正好有 \(k\) 个横着放置,而剩下的 \(\frac{n m}{2}-k\) 个竖着放置,正好铺满台面。现在要你给出对于每组 \(n, m\)\(k\) ,是否有一种方案满足条件。如果有,输出 YES,反之输出 NO

解题思路

构造

分情况讨论:

  • \(n,m\) 都为偶数时,此时可将整个矩形分为多个 \(2\times 2\) 的正方形,由于一个 \(2\times 2\) 的正方形只能同时放两个横或竖者的骨牌,所以要求 \(k\) 为偶数

  • \(n\) 为奇数,\(m\) 为偶数,先考虑 \(k\) 是否能将第一行填满,如不能填满,则不满足要求,\(\color{red}{为什么?}\)假设此时将 \(k\) 个横着的骨牌填满,则必定有一列没有骨牌,由于 \(n\) 为奇数,这时肯定不能只用竖着的骨牌来填,故不满足要求。如果可以填满此时可以忽略第一行,即转化为判断 \(n-1\) 行,\(m\) 列是否可以填满 \(k-m/2\) 个横着的的骨牌,即第一种情况

  • \(n\) 为奇数,\(m\) 为偶数,相当于判断 \(m\) 行,\(n\) 列是否可以填满 \(n*m/2-k\) 个横着的的骨牌,即第二种情况

代码

// Problem: D1. Domino (easy version)
// Contest: Codeforces - Codeforces Round #734 (Div. 3)
// URL: https://codeforces.com/contest/1551/problem/D1
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}


int main()
{
    int t,n,m,k;
    for(cin>>t;t;t--)
    {
    	cin>>n>>m>>k;
    	if(n%2==0&&m%2==0)
    		puts(k%2?"NO":"YES");
    	else if(n&1)
    		puts(k<m/2||(k-m/2)%2?"NO":"YES");
    	else
    	{
			k=n*m/2-k;
			puts(k<n/2||(k-n/2)%2?"NO":"YES");		
    	}
    }
    return 0;
}

D2. Domino (hard version)

即在上题的基础上要求输出字母方案,且相邻骨牌之间表示的字母不同

解题思路

构造

按照上题模拟一遍即可,用字母 \(a\)\(b\) 表示横着的骨牌,\(c\)\(d\) 表示竖着的骨牌,两种骨牌交替使用相应的字母即可,对于 \(n\)\(m\) 不全为偶数的情况,交替处理横着的的第一行后,再开始处理横着的骨牌时,要与使用的第一行开始的骨牌的字母不同,而对于某一行中横着的骨牌处理完后,该行剩余部分得用竖着的骨牌,为了防止跟下一行竖着的骨牌冲突,直接用 \(e\)\(f\) 交替处理该行中剩余的竖着的骨牌

代码

// Problem: D2. Domino (hard version)
// Contest: Codeforces - Codeforces Round #734 (Div. 3)
// URL: https://codeforces.com/contest/1551/problem/D2
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

int main()
{
    int t,n,m,k;
    for(cin>>t;t;t--)
    {
    	cin>>n>>m>>k;
    	vector<string> res(n,string(m,'?'));
    	bool f=false,ff=false;
    	int y=0;
    	if(n%2==0&&m%2==0)
    	{
    		if(k%2)
    		{
    			puts("NO");
    			continue;
    		}
    		puts("YES");
    	}
    	else
    	{
    		if(m&1)
    		{
    			swap(n,m),k=n*m/2-k;
    			res=vector<string>(n,string(m,'?'));
    			f=true;
    		}
    		if(k<m/2||(k-m/2)%2)
    		{
    			puts("NO");
    			continue;
    		}
    		ff=true;
    		puts("YES");
    		k-=m/2;
    		char c='a';
    		for(int i=0;i<m;i+=2)res[0][i]=res[0][i+1]=c,c='a'+'b'-c;
    	}
    	int x=ff;
    	char c='b';
    	for(int i=ff;i<n&&k;i+=2)
    		for(int j=0;j<m;j+=2)
    		{
    			if((j>>1&1)==0)c='b';
    			else
    				c='a';
    			res[i][j]=res[i][j+1]=c;
    			res[i+1][j]=res[i+1][j+1]='a'+'b'-c;
    			k-=2;
    			if(k<=0)
    			{
    				x=i,y=j+2;
    				break;
    			}
    		}
    	c='e';
    	for(int j=y;j<m;j+=2)
    	{
    			if(x+1<n)
    			{
    				res[x][j]=res[x+1][j]=c;
    				res[x][j+1]=res[x+1][j+1]='e'+'f'-c;
    			}		
    	}
    	for(int i=x+2;i<n;i+=2)
    		for(int j=0;j<m;j+=2)
    		{
    				if(i>>1&1)c='c';
    				else
    					c='d';
    				res[i][j]=res[i+1][j]=c;
    				res[i][j+1]=res[i+1][j+1]='c'+'d'-c;			
    		}
    	if(f)
    	{
    		for(int j=0;j<m;j++)
    		{
    			for(int i=0;i<n;i++)cout<<res[i][j];
    			puts("");
    		}
    	}
    	else
    		for(int i=0;i<n;i++)cout<<res[i]<<'\n';
    }
    return 0;
}

E. Fixed Points

一个整数序列 \(a 1, a 2, \ldots, a_{n}\) ,一次操作,可以删除一个数,然后该数右侧的数向左移动一个单位。对于一 个长度为 \(n\) 的整数序列 \(b_{i}\) ,求最少需要删除几个数后,会有至少 \(k\)\(i\) 满足 \(b_{i}=i\)

输入格式:

第一行一个正整数 \(t(1 \leq t \leq 100)\) 表示数据组数。
对于每组数据,第一行两个正整数 \(n, k\) 分别表示整数序列的长度,以及至少满足 \(b_{i}=i\) 的个数。
保证 \(n\) 在测试数据中的总和不超过 2000 。

输出格式:

对于每组数据,

  • 如果无解,输出 \(-1\)
  • 否则,一个整数表示最小的删除次数,

说明/提示:

对于第一个测试数据,序列不满足所需条件,但可以通过删除第一个数来提供,序列为 \([1,2,3,4,5,6]\) , 有 \(6\) 个数满足条件。
对于第二个测试数据,有两种方法:第一种是删除 \(a_{1}\)\(a_{3}\) ;第二种是删除 \(a_{2}\)\(a_{3}\)

解题思路

dp

  • 状态表示:\(f[i][j]\) 表示前 \(i\) 个数删除 \(j\) 个数后对应下标相等的最多个数

  • 状态计算:\(f[i][j]=max(f[i-1][j-1],f[i-1][j]+(a[i]==(i-j)))\)
    分析:枚举最后删除的数是否是最后一个数,如果删除的是最后一个数,则最后一个数没有贡献,即该删除操作浪费了,且对前面的数的贡献没有影响即 \(f[i-1][j-1]\),如果删除的不是最后一个数,由于要删除的是 \(j\) 个数,则最终最大的下标为 \(i-j\),最后一个数的贡献为 \(a[i]==i-j\),而前 \(i-1\) 个数的贡献为 \(f[i-1][j]\),此时最大下标为 \(i-1-j\),所以最后一个数的贡献对前面的数没有影响,故总的贡献为 \(f[i-1][j]+(a[i]==i-j)\)
    最后,在 \(f[n][i]\) 中找出满足 \(f[n][i]\geq k\) 的最小 \(i\) 即可

  • 时间复杂度:\((n^2)\)

代码

// Problem: E. Fixed Points
// Contest: Codeforces - Codeforces Round #734 (Div. 3)
// URL: https://codeforces.com/contest/1551/problem/E
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=2005;
int t,n,k,a[N],f[N][N];
int main()
{
    help;
    for(cin>>t;t;t--)
    {
    	cin>>n>>k;
    	for(int i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		for(int j=0;j<=i;j++)
    			f[i][j]=max(f[i-1][j-1],f[i-1][j]+(a[i]==i-j));
    	}
    	int res=-1;
    	for(int i=0;i<=n;i++)
    		if(f[n][i]>=k)
    		{
    			res=i;
    			break;
    		}
    	cout<<res<<'\n';
    	for(int i=1;i<=n;i++)
    		for(int j=0;j<=i;j++)f[i][j]=0;
    }
    return 0;
}
posted @ 2022-05-19 01:17  zyy2001  阅读(40)  评论(0编辑  收藏  举报