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;
}