题目
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;
}
一言
我愿以身化剑……永生永世护你周全! ——无限恐怖