#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
",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
", 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

", 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 imes (b+c)=a imes b+a imes 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;
}
原文地址:https://www.cnblogs.com/Cao-Yucong/p/13758821.html