#QBXT2020十一 Day1 考试题解
\(Day 1\)
T1 打扑克
手推可以得到结果,注意特别判定\(n = 1,2,3\)时的情况
经过手推,可以得到当且仅当n是奇数且0是先手,0才会获胜。
\(Code\)
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100010
using namespace std;
char c[N];
int T,op;
int main()
{
cin>>T;
while(T--)
{
scanf("%s%d",c,&op);
int n=strlen(c);
int x=c[n-1];
if(n==1&&x=='2')
{
if(!op) cout<<0<<endl;
else cout<<1<<endl;
}
else
{
if((x-'0')%2==1&&!op) cout<<0<<endl;
else cout<<1<<endl;
}
}
}
T2 粉刷匠
通过审题,能够知道,后面的操作是要把前面的操作完全覆盖的。所以我们可以倒着看这些操作,对于每一种操作,记录当前颜色(即最终颜色)并删除这一列。这样进行计算就可以了。
\(Code\)
#include <iostream>
#include <algorithm>
using namespace std;
const long long maxn = 1000009;
long long read() {
char c = getchar();
long long f = 1, tot = 0;
if (c == '-') { f = -1; c = getchar(); }
while (c >= '0' && c <= '9') {
tot = tot * 10 + c - '0';
c = getchar();
}
return tot * f;
}
long long n, m, k;
long long x[maxn], y[maxn], z[maxn], totx, toty, wx, wy;//横行是x,纵列是y
bool visx[maxn], visy[maxn];
long long ans = 0;
int main() {
n = read(), m = read(), k = read();
for (long long i = 1; i <= k; ++i) x[i] = read(), y[i] = read(), z[i] = read();
wx = n, wy = m;
for (long long i = k; i >= 1; --i) {
if (!x[i]) {
if (!visx[y[i]] && wy) {
visx[y[i]] = 1;
if (z[i]) ans += wy;
wx--;
}
} else {
if (!visy[y[i]] && wx) {
visy[y[i]] = 1;
if (z[i]) ans += wx;
wy--;
}
}
}
cout << ans << endl;
return 0;
}
倒序操作类似题目:\(BZOJ 2054\)疯狂的馒头
T3 直线竞速
离线算法。将所有询问按照时间顺序排序,每次对于新的询问,都用冒牌排序进行更新,从而求出第\(k\)的值。
\(Code\)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#include<cstdlib>
#include<bitset>
#include<stack>
#include<ctime>
#include<fstream>
#define dd double
#define ll long long
#define mp make_pair
#define pb push_back
#define N 7010
#define M 1010
using namespace std;
int n,Q;
struct ma
{
int v,a,num;
}w[N];
bool cmp1(ma x,ma y)
{
return x.a>y.a;
}
struct am
{
int t,k,ans,num;
}q[N];
bool cmp2(am x,am y)
{
return x.t<y.t;
}
bool cmp3(am x,am y)
{
return x.num<y.num;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&w[i].v,&w[i].a);
w[i].num=i;
}
cin>>Q;
for(int i=1;i<=Q;i++)
{
scanf("%d%d",&q[i].t,&q[i].k);
q[i].num=i;
}
sort(w+1,w+n+1,cmp1);
sort(q+1,q+Q+1,cmp2);
for(int i=0;i<=Q;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=j;k>1;k--)
{
ll x=w[k].a+(ll)w[k].v*q[i].t;
ll y=w[k-1].a+(ll)w[k-1].v*q[i].t;
if(y<x||y==x&&w[k-1].num>w[k].num) swap(w[k-1],w[k]);
else break;
}
}
q[i].ans=w[q[i].k].num;
}
sort(q+1,q+Q+1,cmp3);
for(int i=1;i<=Q;i++) printf("%d\n",q[i].ans);
}
T4 游戏 (luogu)
考场思路:四维\(DP\),但是写挂了。
\(Code\)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 2009;
int m, n;
int a[maxn], b[maxn];
int ans = 2147483647;
int sa[maxn], sb[maxn];
int f[maxn][maxn];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= m; ++i) cin >> b[i];
reverse(b + 1, b + 1 + m);
// for (int i = 1; i <= m; ++i) cout << b[i] << ' '; cout << endl;
for (int i = 1; i <= max(m, n); ++i) {
sa[i] = sa[i - 1] + a[i];
sb[i] = sb[i - 1] + b[i];
cout << sa[i] << ' ' << sb[i] << endl;
}
for (int i = 1; i <= n; ++i) for (int j = 1;j <= m; ++j) f[i][j] = 0x3ffff;
f[1][1] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
for (int k = 1; k < i; ++k) {
for (int l = 1; l < j; ++l) {
printf("%d %d %d %d %d %d %d\n", i, j, k, l, f[i][j], (sa[i] - sa[k - 1] - (i - k + 1)) , (sb[j] - sb[l - 1] - (j - l + 1)));
f[i][j] = min(f[i][j], f[k][l] + (sa[i] - sa[k - 1] - (i - k + 1)) * (sb[j] - sb[l - 1] - (j - l + 1)));
printf("%d %d %d %d %d %d %d\n\n", i, j, k, l, f[i][j], (sa[i] - sa[k - 1] - (i - k + 1)) , (sb[j] - sb[l - 1] - (j - l + 1)));
}
}
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) cout << f[i][j] << ' '; cout << endl;
}
cout << f[n][m] << endl;
return 0;
}
首先,由于(s2-k2)(s1-k1)每次加上一个数都要减去1,我们可以预处理全部减一,每次的操作得分就变成s2s1。
因为每个数都是正整数,对于数列1中的连续两段数a,c数列2中的连续两段数b,d易得(a+c)*(b+d)>=ab+cd。所以当每次两个数列都只取一个数时结果最小。
但是,两个数列不等长,无法一对一消除。所以可能出现一对多的情况。
综上,使用DP
F[i][j]表示一数列已删去i个数另一数列已删去j个数时的最优解。
\[F[i][j]=min{f[i-1][j-1],f[i-1][j],f[i][j-1]}+a[i]*b[j];
\]
当两边都只取一个数的时候
\[f[i][j]=f[i-1][j-1]+a[i]*b[j];
\]
当其中一个数列取多个数的时候 \(f[i][j]=f[i][j-1]+a[i]*b[j]\) 表示a中只取一个数,b中可能取多个数,由乘法分配律\(a\times (b+c)=a\times b+a\times c\)得相对于f[i][j-1]增加的数为a[i]*b[j]
初始化:f=maxlint,f[0][0]=0。
\(Code\)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#include<cstdlib>
#include<bitset>
#include<stack>
#include<ctime>
#include<fstream>
#define dd double
#define ll long long
#define mp make_pair
#define pb push_back
#define N 2010
#define M 1010
using namespace std;
int n,m;
int a[N],b[N];
int f[N][N],g[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]--;
}
for(int i=1;i<=m;i++)
{
scanf("%d",&b[i]);
b[i]--;
}
memset(f,0x3f,sizeof(f));
memset(g,0x3f,sizeof(g));
f[0][0]=g[0][0]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int x=a[i]*b[j];
f[i][j]=min(f[i][j-1],min(f[i-1][j-1],g[i-1][j-1]))+x;
g[i][j]=min(g[i-1][j],min(f[i-1][j-1],g[i-1][j-1]))+x;
//cout<<i<<' '<<j<<' '<<f[i][j]<<' '<<g[i][j]<<endl;
}
int ans=min(f[n][m],g[n][m]);
cout<<ans<<endl;
}