[20181017][模拟赛]

题目

T1

思路

统计每列有多少个1。如果(sum[0]geqslant sum[1]),那么ans的这一位为0,否则为1。

预计得分:100

实际得分:100

代码

#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 1010;
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
 	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}
int a[N];
char s[N];
int main() {
	freopen("curse.in","r",stdin);
	freopen("curse.out","w",stdout);
	int n = read();
	int L;
	for(int i = 1;i <= n;++i) {
		scanf("%s",s+1);
		L = strlen(s + 1);
		for(int j = 1;j <= L;++j)
			a[j] += s[j] - '0'; 
	}
	for(int i = 1; i <= L; ++i) {
		if(a[i] > n/2) printf("1");
		else printf("0");
	}
	return 0;
}

T2

20分思路

首先,分析一下题目,其实R和G的数据范围是假的。因为这个题可以转化为将这个序列分为R+G段,其中小于L的段必须大于等于R。并且所有的段都要小于2*L。那么如果R+G>=n,可以直接输出1,就有了20分。

60-70分思路

一般看到题目就能想到二分答案,把L二分出来,然后check。考虑怎样check。继续上面的思路思考,把这个题想成是“将这个序列分为R+G段,其中小于L的段必须大于等于R。并且所有的段都要小于2L”。然后考虑dp转移,用f[i][j]表示前i个分为j段(保证每段都小于等于2L)其中小于等于L的段的个数最后只要判断f[n][R+G]是不是大于等于R就行了。

转移方程:(f[i][j]=f[k][j-1]+(a[i]-a[k+1])<L (0leqslant k leqslant i-1))

60-70分代码

//f[i][j]表示前i段分成j段每段都小于等于2L,小于等于L的段的数量。
//如果f[n][R+G] < R表示no,否则为yes 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 2010,INF = 1000000000 + 100;
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}
int R,G;
int a[N];
int f[N][N*2];
int n;
int check(int L) {
	memset(f,-1,sizeof(f));
	f[0][0] = 0;
	for(int i = 1; i <= n; ++i) {
		for(int j = 1;j <= R + G; ++j) {
			for(int k = i - 1;k >= 0; --k) { 
				if(a[i] - a[k + 1] >= 2 * L ) break;
				if(f[k][j-1] == -1) continue;
				f[i][j] = max(f[i][j],f[k][j - 1] + (a[i] - a[k + 1] < L));
				f[i][j] = max(f[i][j],f[i][j - 1]);
			}
		}
	}
	return f[n][R + G] >= R ;
}
int main() {
//	freopen("light.in","r",stdin);
//	freopen("light.out","w",stdout);
	n = read(),R = read(), G = read();
	if(R + G >= n) {
		puts("1");
		return 0;
	}
	a[n+1] = INF;
	for(int i = 1;i <= n; ++i) a[i] = read();
	sort(a + 1, a + n + 1);
	int l = 1, r = a[n];
	int ans = 0;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if(check(mid)) ans = mid, r = mid - 1;
		else l = mid + 1;
	}
	cout<<ans;
//	check(4);
	return 0;
}

假的100思路

在上面的基础上加上一些优化。首先需要把dp中的i和j的枚举顺序颠倒,也就是先枚举j再枚举i,然后发现每一层都是从上一层转移过来的,所以压掉第一维。然后就有可以发现可以用单调队列优化。然后就可以愉快的再(O(n^2))的时间复杂度中进行dp。

调了一个下午好不容易调出来,竟然80分fu*k!!!

正解

看完正解才知道自己多么zz。用f[i][j]表示用了i次L,j次2L可以调到的最远距离。用q[i]表示从i开始用L跳一步可以跳到的最远距离,p[i]表示从i开始用2L跳一步可以跳到的最远距离。转移的时候(f[i][j]=max(q[f[i-1][j]+1],p[f[i][j-1]+1]))

最后判断f[R][G]是不是大于等于n就行了。

预计得分 50

实际得分 70

ac代码

//f[i][j]表示前i段分成j段每段都小于等于2L,小于等于L的段的数量。
//如果f[n][R+G] < R表示no,否则为yes 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 20010,INF = 1000000000 + 100;
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}
int R,G;
int a[N];
int f[N];
int n;
int qL[N],q2L[N],qLhead,q2Lhead,qLtail,q2Ltail;
int check(int L) {
	memset(f,-1,sizeof(f));
	f[0] = 0;
	for(int j = 1;j <= R + G; ++j) {
		qLhead = q2Lhead = 1;
		qLtail = q2Ltail = 0;
		int nowL = n - 1,now2L = n - 1;
		for(int i = n; i >= 1; --i) {
			while(qL[qLhead] >= i && qLhead <= qLtail) 
			qLhead++;
			while(a[i] - a[nowL + 1] < L && nowL >= 0) {
				while(f[qL[qLtail]] <= f[nowL] && qLtail >= qLhead) 
				qLtail--;
				qL[++qLtail] = nowL--;
			} 
			while(q2L[q2Lhead] >= i && q2Lhead <= q2Ltail) 
			q2Lhead++;
			while(a[i] - a[now2L + 1] < 2 * L && now2L >= 0) {
				while(f[q2L[q2Ltail]] <= f[now2L] && q2Ltail >= q2Lhead) 
				q2Ltail--;
				q2L[++q2Ltail] = now2L--;				
			}
			int k1 = qL[qLhead],k2 = q2L[q2Lhead];
			if(f[k1] != -1) f[i] = max(f[i],f[k1] + 1);
			if(f[k2] != -1) f[i] = max(f[i],f[k2]);
		}
		f[0] = -1;
	}
	return f[n] >= R ;
}
int main() {
	n = read(),R = read(), G = read();
	if(R + G >= n) {
		puts("1");
		return 0;
	}
	a[n+1] = INF;
	for(int i = 1;i <= n; ++i) a[i] = read();
	sort(a + 1, a + n + 1);
	int l = 1, r = a[n];
	int ans = 0;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if(check(mid)) ans = mid, r = mid - 1;
		else l = mid + 1;
	}
	cout<<ans;
	return 0;
}

真AC代码

//f[i][j]表示前i段分成j段每段都小于等于2L,小于等于L的段的数量。
//如果f[n][R+G] < R表示no,否则为yes 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fi(s) freopen(s,"r",stdin);
#define fo(s) freopen(s,"w",stdout);s
using namespace std;
typedef long long ll;
const int N = 2010,INF = 1000000000 + 100;
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}
int f[N][N],a[N],R,G,q[N],p[N],n;
int check(int L) {
	memset(f,0,sizeof(f));
	for(int i = 1; i <= n;++i) {
		for(int j = i;j <= n;++j) {
			if(a[j] - a[i] + 1 <= L) q[i] = j;
			if(a[j] - a[i] + 1<= L*2) p[i] = j;
		}
	}
	q[n + 1] = n; p[n + 1] = n;
	for(int i = 0;i <= R; ++i) {
		for(int j = 0;j <= G; ++j) {
			if(i - 1 >= 0) f[i][j] = max(f[i][j], q[f[i - 1][j] + 1]);
			if(j - 1 >= 0) f[i][j] = max(f[i][j], p[f[i][j - 1] + 1]);
		}
	}
	return  f[R][G] >= n;
}
int main() {
	fi("light.in");
	fo("light.out");
	n = read(),R = read(), G = read();
	if(R + G >= n) {
		puts("1");
		return 0;
	}
	a[n+1] = INF;
	for(int i = 1;i <= n; ++i) a[i] = read();
	sort(a + 1, a + n + 1);
	int l = 1, r = a[n];
	int ans = 0;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if(check(mid)) ans = mid, r = mid - 1;
		else l = mid + 1;
	}
	cout<<ans;
	return 0;
}

T3

思路

次短路的模板。从1节点和n节点分别跑最短路,然后枚举条边x,用dis[1][x]+w[x]+dis[x][n],从中取最小并且不等于最短路的即可。

然而freopen写成了这样$$freopen("maze.out","w",stout)$$

愉快的ce了

预计得分 80

实际得分 0

代码

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 10010;
ll read() {
	ll x = 0,f = 1;char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}
struct node {
	int u,v,nxt,w;
}e[100000 * 2 +100];
int ejs = -1,head[N],fa[N];
void add(int u,int v,int w) {
	e[++ejs].u = u,e[ejs].v = v;e[ejs].w = w; e[ejs].nxt = head[u];head[u] = ejs;
}
int n,m;
queue<int>q;
int vis[N];
ll dis[4][N];
void spfa(int U,int bz) {
	while(!q.empty()) q.pop();
	memset(vis,0,sizeof(vis));
	q.push(U);
	dis[bz][U] = 0;
	while(!q.empty()) {
		int u = q.front();
		vis[u] = 0;
		q.pop();
		for(int i = head[u];i != -1;i = e[i].nxt) {
			int v = e[i].v;
			if(dis[bz][v] > dis[bz][u] + e[i].w) {
				dis[bz][v] = dis[bz][u] + e[i].w;
				if(!vis[v]) {
					q.push(v);
					vis[v] = 1;
				}
			}
		}
	}
	return ;
}
int main() {
	freopen("maze.in","r",stdin);
	freopen("maze.out","w",stdout);
	n = read(),m = read();
	memset(head,-1,sizeof(head));
	memset(dis,0x3f,sizeof(dis));
	for(int i = 1;i <= m;++i) {
		int u =read(),v = read(),w = read();
		add(u,v,w);	add(v,u,w);
	}
	spfa(1,1);
	spfa(n,2);
	int k = dis[1][n];
	ll ans = 0x7fffffff;
	for(int i = 0;i <= ejs;++i)
		ans = dis[1][e[i].u] + dis[2][e[i].v] + e[i].w == k ? ans : min(ans,dis[1][e[i].u] + dis[2][e[i].v] + e[i].w);
	cout<<ans;
	return 0;
}

一言

我愿以身化剑……永生永世护你周全! ——无限恐怖

原文地址:https://www.cnblogs.com/wxyww/p/9806612.html