5.18周赛

反思一下,A题要不是第三个样例可能就WA不知道多少分了,即使有第三个样例step2 - (x + y)) % 2要等于0我也推了好久(差点没整出来)
然后一直在B,C之间犹豫,想一会儿C想一会儿B的,结果差点两道题都没有做出来
B题出了好几个智障错误:
从1开始A,B一起枚举结果忘了i - A或者i - B可能(其实是一定)会小于0然后出现未知错误,搞了好久分开枚举才过了样例,这么点小事还是考试之后Phenning巨佬告诉我的
枚举水的时候误以为只要是一次性的背包就都从大到小枚举,忽略了一次性背包问题枚举的本质(不能让一个东西被反复更新)因为/2是一个变小的行为,所以需要从小到大枚举才不会导致同一个被更新了一次又一次
结果没时间做C了,光荣垫底orz orz orz
C题即使是在考试之后得知Tarjan也没做出来orz orz orz
果然还是我太弱了(反思),平时做的题也太少了,过于依赖讲义,评讲什么的,,,,,一到自己做题就完全懵逼了,,,orz orz orz

A

显然时间等于走的步数

观察一下样例2

发现只用用step2,x2,y2记录一个上一次的步数和坐标,再与这一次的step,x,y比较一下,如果(step - step2) < abs(x2 - x) + abs(y2 - y)就标记一个flag = 1;

观察样例3

if((step2 - (x + y)) % 2)也是不行的,也需要标记flag = 1(因为类似于“消磨时间”的走法一定是走偶数步,所以步数与位置的曼哈顿距离必须同奇偶)
边输入边扫,扫完之后看一下flag,如果等于0就输出Yes,等于1就输出No


B

一个最简单的NKOJ上的背包问题1加背包问题2的联合
A,B分别为无限物品,水为仅能用一次的有限物品

具体过程
v[0] = true;
for(int i = A; i <= T; i ++)if(v[i - A])v[i] = true;
for(int i = B; i <= T; i ++)if(v[i - B])v[i] = true;

for(int i = 0; i <= T; i ++)if(v[i])v[i / 2] = true;
//注意一下,由于这里i是除法,也就是越更新越小,所以for循环从小到大枚举i
//举个栗子:假设f[6] = true,我们将f[3]也标记为true

for(int i = A; i <= T; i ++)if(v[i - A])v[i] = true;
for(int i = B; i <= T; i ++)if(v[i - B])v[i] = true;
//再来一遍就可以了orz orz orz

C

每个人都有一个固定的信息传递对象 —> 每个人的出度都是1
找最少进行几轮可以转换成最少经过几个人—>找最小环
因为每个人的出度都是1,所以不会出现环套环的情况,即可以求最小环
解法:用Tarjan找强连通分量,每一个强连通分量都是一个环,在从栈里面弹出点的时候记录一下每一个强连通分量内点的个数,只要不是1就比较出最小的一个


D

一道没有人通过的水题orz(当然我是完全不会的)
如果只有一种球:逆序对(凡是交换相邻两个的都是求逆序对) —> 两种球:两个逆序对orz
设白球:1 ~ i个
黑球:1 ~ j个
无论怎么排,一定会占满2n个里面前i + j个位置
所以可以设一个状态f[i][j],表示前i加j个位置中放白球前i个,黑球前j个,所需最少交换次数
决策:i + j号位置放i号白球还是j号黑球
方程:f[i][j] = min{f[i - 1][j] + cntWhite[i][j],f[i][j -1] + cntBlack[i][j]}
cntWhite[i][j] = ① + ②:
①[1,i - 1]白球中,位于i右侧的,都要调整到i左侧(逆序对)
②[1, j]黑球中,位于i右侧的,都要调整到i左侧(逆序对)
cntBlack[i][j] = ③ + ④:
③[1, i]白球中,位于j右侧的,都要调整到j左侧
④[1,j - 1]黑球中,位于j右侧的,都要调整到j左侧
如何求cnt?——暴力枚举!

cntW的求法
for(int i = 0; i <= n; i ++){
    	for(int j = 1; j < i; j ++)
            if(posw[i] < posw[j]) cntW[i][0] ++;
	
        for(int j = 1; j <= n; j ++){
        	cntW[i][j] = cntW[i][j - 1];
        	if(posw[i] < posb[j]) cntW[i][j] ++;
    	}
    }
cntB的求法
for(int i = 0; i <= n; i ++){
	for(int j = 1; j < i; j ++)
		if(posb[i] < posb[j]) cntB[0][i] ++;
			
	for(int j = 1; j <= n; j ++){
		cntB[j][i] = cntB[j - 1][i];
		if(posb[i] < posw[j])cntB[j][i] ++;
	}
}

一般来说数组是存对应位置是哪个球
但是这里我们存的是对应编号是哪个位置
感觉是这辈子自己都想不出来的orz orz orz


AC代码如下

A

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
int n, x, y, step1, step2;
int lastx, lasty;
bool flag = false;
int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++){
		scanf("%d%d%d", &step2, &x, &y);
		if((step2 - step1) < abs(lastx - x) + abs(lasty - y))flag = true;
		else if((step2 - x - y) % 2)flag = true;
		step1 = step2;
		lastx = x;
		lasty = y; 
	}
	
	if(flag) puts("No");
	else puts("Yes");
	
	return 0;
} 

B

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;

int T, A, B, Max = 0;
bool v[5000005]; 

int main(){
	v[0]=true; 
	
	scanf("%d%d%d", &T, &A, &B);
	
	if(!(T % A)|| !(T % B)){
		printf("%d\n",T);
		return 0;
	}
	
	for(int i = A; i <= T; i ++) if(v[i - A])v[i] = 1;
		
	for(int i = B; i <= T; i ++) if(v[i - B])v[i] = 1;

	for(int i = 1; i <= T; i ++) if(v[i]) v[i / 2] = true;	
	
	for(int i = A; i <= T; i ++) if(v[i - A])v[i] = 1;
		
	for(int i = B; i <= T; i ++) if(v[i - B])v[i] = 1;
		
	for(int i = T; i >= 1; i --){
		if(v[i]){printf("%d",i);
                return 0;
	}
	
}

C

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;

stack<int>S; 
bool InStack[200005];
int n, a, scc, tot;
int VisitTime, MIN = 9999999;
int End[200005], Last[200005], Next[200005];
int low[200005], dfn[200005], Belong[200005];

int Min(int a, int b){
	return a < b ? a : b;
}

void tajian(int u){
	int nonstop = 0;
	low[u] = dfn[u] = ++ VisitTime;
	S.push(u);
	InStack[u] = true;
	for(int i = Last[u]; i; i = Next[i]){
		int v = End[i];
		if(!dfn[v]){
			tajian(v);
			low[u] = Min(low[u], low[v]);
		}
		else if(InStack[v]){
			low[u] = Min(low[u], low[v]);
		}
	}
	if(dfn[u] == low[u]){
		scc ++;
		int v;
		do{
			v = S.top();
			S.pop();
			InStack[v] = false;
			Belong[v] = scc;
			nonstop ++;
		}while(u != v);
		if(nonstop != 1)MIN = Min(nonstop, MIN);
	}
}



int main(){
	scanf("%d", &n);
	
	for(int i = 1; i <= n; i ++){
		scanf("%d", &a);
		End[i] = a;
		Next[i] = Last[i];
		Last[i] = i;	
	}
	
	for(int i = 1; i <= n; i ++)if(!dfn[i])tajian(i);
	
	printf("%d", MIN);
}

D

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;

char c; 
int n, x;
int posw[2005], posb[2005];
int cntW[2005][2005],cntB[2005][2005],f[2005][2005];

int main()
{
	memset(f,0x7f,sizeof(f));
	
	cin>>n;
	
	for(int i = 1; i <= 2 * n; i ++){
    	cin>>c>>x;
    	if(c == 'W') posw[x] = i;
    	else posb[x] = i;
	}
	
	for(int i = 0; i <= n; i ++){//i从0开始! 
    	for(int j = 1; j < i; j ++) if(posw[i] < posw[j]) cntW[i][0] ++;
        for(int j = 1; j <= n; j ++) cntW[i][j] = cntW[i][j - 1] + (posw[i] < posb[j]);
    }

	for(int i = 0; i <= n; i ++){
		for(int j = 1; j < i; j ++) if(posb[i] < posb[j]) cntB[0][i] ++;
		for(int j = 1; j <= n; j ++) cntB[j][i] = cntB[j - 1][i] + (posb[i] < posw[j]);
	}

	
	f[0][0] = 0;//~赋~初~值~ 
	
	for(int i = 0; i <= n; i ++)
		for (int j = 0; j <= n; j ++)
	{
		if(i)f[i][j] = min(f[i - 1][j] + cntW[i][j], f[i][j]);
		if(j)f[i][j] = min(f[i][j - 1] + cntB[i][j], f[i][j]);
	}
	
	printf("%d\n",f[n][n]);
	
	return 0; 
}
posted @ 2019-05-20 18:21  羽错光阴  阅读(137)  评论(0编辑  收藏  举报