初见安~据官方声称,本场考试的每个题目都出自随机题库,所以——深感第一题和第二题都应该是正常考试的第一题难度啊!!!【然而我竟然都没做出??】
传送门:入门OJ P2119
Seq
Description
由于hyf长得实在是太帅了,英俊潇洒,风流倜傥,人见人爱,花见花开,车见车载。有一群MM排队看hyf。每个MM
都有自己独特的风格,由于hyf有着一颗包容的心,所以,什么风格的MM他都喜欢……但是,hyf有一个特别的要求
,他不希望总是看到风格得差不多的MM,更加特别的是,如果两个MM风格完全一样,hyf不会有任何意见。现在,h
yf希望从去看他的MM中,去掉一些MM,从而使得相邻2个MM的风格值的差(绝对值)不为1。自然地,hyf希望去掉
的MM越少越好。
Input
第一行一个整数N;
第2~N+1行N个整数,第i个为ci。表示第i个MM的风格值。
N≤1000 0 ≤ ci ≤ 2000
Output
一个数,表示最少要去掉的MM数。
Sample Input
6
4
2
2
1
1
1
Sample Output
2
Sol
看了整整20min都毫无头绪的一个题……其实前不久做过一个求最长连续上升子序列的问题,感觉很像,但是仍然是找不到任何关联……看了题解恍然大悟——比求最长连续上升子序列简单多了好嘛!!直接像求最长上升子序列那样nlogn求,改一下条件就行了!!!用dp来线性维护到点i的最长相邻不相差一的序列长度。
但是有一个小问题——有可能同样的长度,一个是以与当前值相差一结尾的,一个是以不相差一结尾的,如果后者在前那么就会被覆盖掉,答案会出错。所以【本狸的做法】存的时候存时间线上的三个值,因为顶多一个多一,一个少一,总有一个合法的。
上我代码【其实考试的时候卡内存,1M,我这个代码估计是会被MLE的QwQ毕竟怎么算也有2M的样子……?】
#include<bits/stdc++.h>
#define maxn 200005
using namespace std;
int read() {
int x = 0, ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x;
}
int n, a[maxn], dp[maxn], t = 0;
map<int, int> d1, d2, d3;//d[i]存的是值为i时结尾的那个数的下标
int main() {
// freopen("out.txt", "w", stdout);0
n = read();
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= n; i++) {
dp[i] = 1;
for(int j = t; j > 0; j--) {
if(abs(a[i] - a[d1[j]]) != 1) {//依次枚举
dp[i] = dp[d1[j]] + 1; break;
}
else if(d2[j] && abs(a[i] - a[d2[j]]) != 1) {//注意这里一定要判断d2
和d3的存在
dp[i] = dp[d2[j]] + 1; break;
}
else if(d3[j] && abs(a[i] - a[d3[j]]) != 1) {
dp[i] = dp[d3[j]] + 1; break;
}
}
t = max(t, dp[i]);
d3[dp[i]] = d2[dp[i]];//更新时间线
d2[dp[i]] = d1[dp[i]];
d1[dp[i]] = i;
}
printf("%d\n", n - t);
return 0;
}
然后就水过去了QwQ
传送门:入门OJ P2975
photo
Description
有N个人,来自K个家族.他们排成一行准备照相,但是由于天生的排外性,每个人都希望和本家族的人站在一起,中间不要加入别的家族的人.问最少从队列中去掉多少个就可以达到这个目的.
Input
第一行给出N,K。N在[1,100],K在[1,5]
第二行给出N个数,每个数为1到K中的某个数。
Output
最少从队列中去掉多少个就可以达到这个目的
Sample Input
10 3
2 1 2 2 1 1 3 1 3 3
Sample Output
2
//【说明】
//去掉第二个人和第七个人就可以了。
Sol
看起来和第一题很像——都是那种题目简单,看似考思维并且写出来应该很简单的题。然后我就又没做出来。
确实很像——又是一个dp题……用dp维护到达这个人时最多可以保留的人数。
每个人可以选择留还是不留,这就要看哪种更优。留下,则要保证之前这个家族没有出现过,或者和前一个人是同一个家族【因为前一个人往前推,这一群人的第一个人是满足第一条要求的】。如果不留,就很简单——直接继承到达前一个人时的最长长度。
也就是说我们要参考的量有:目前已经出现的家族的状态和前一个人的家族。所以开一个表示到第i个人,家族状态为j【二进制状压】,前一个人为家族k时最多可以保留的人数。状态转移就很简单了:
首先初始化【可以直接用memcpy】,然后处理满足条件2的情况:和前者家族相同:
接下来我们考虑条件1——因为家族之前没有出现过,所以当前状态一定满足这个家族出现过了。
,l是我们枚举的前一个人的家族。确实,直接枚举就可以了。
最后取,用n减去即可。
上代码——
#include<bits/stdc++.h>
#define maxn 105
using namespace std;
int read() {
int x = 0, ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x;
}
int n, k, a[maxn];
int dp[maxn][1 << 6][6];
int main() {
n = read(), k = read();
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i <= n; i++) {
memcpy(dp[i], dp[i - 1], sizeof dp[i - 1]);//memcpy很方便的……
for(int j = 0; j < (1 << k); j++) {
if(j & (1 << a[i] - 1)) {//保证当前状态有这个家族
dp[i][j][a[i]] = max(dp[i][j][a[i]], dp[i - 1][j][a[i]] + 1);
for(int l = 0; l <= k; l++)//枚举前导家族
dp[i][j][a[i]] = max(dp[i][j][a[i]], dp[i - 1][j ^ (1 << a[i] - 1)][l] + 1);
}
}
}
int ans = 0;
for(int j = 0; j < (1 << k); j++)
for(int l = 0; l <= k; l++)
ans = max(ans, dp[n][j][l]);
printf("%d\n", n - ans);
return 0;
}
传送门:入门OJ P2127
分组行动
Description
最近,木木中学要举行一年一度的辩论赛了,我们活泼开朗乐观向上不寂寞不生病不挂科天天回家吃饭的新时代好
少年--飞飞,自然是热情参与咯!辩论嘛,就有正方和反方两个组,这是一个传统项目,所以,包括飞飞,木木中
学的每一个学生都会加入2个组中的一个,不会有人打酱油的(如果有人打酱油,那么飞飞会义无反顾义不容辞的
上前用一翻惊天动地的演说打消他打酱油的念头的)。自然啦,作为有思想有文化能言善辩的好少年,飞飞,其实
加入哪个组,从技术角度分析真是无所谓的,不过呢,从另外一些角度来看,就复杂的多了……比如,飞飞是个很
不喜欢八卦的正义青年,所以啊,飞飞很不想和年级最PP的女生青菜分在一个组,因为会产生八卦的--为什么会有
八卦?首先是他们比较熟(比较熟就会有八卦吗?无限yy中……),最重要的就是因为飞飞是大帅哥啊!人长得帅
,有时候真是挺麻烦的,尤其飞飞还如此低调……再比如,飞飞也不想和小邱分在一个组--虽然他们不认识,但是
,飞飞听说小邱很懒还很暴力,飞飞不喜欢暴力……当然了,不论如何的分组行动,都会产生一些代价的,比如两
个好朋友分在了不同的组,那肯定不是很高兴了。飞飞知道,学校里有M对同学是相互认识的,每对认识的同学间
,都有一个要好程度C,自然的,关系越好,要好程度越高,也越不愿意分开。对于一个分组方案,必然会使得有
某些认识的同学分开,飞飞认为,一个分组方案的代价就是最不愿意分开的那两个同学之间的要好程度。飞飞在想
,和青菜MM分在不同的组最小代价是多少呢?和小邱同学分在不同的组最小代价是多少呢?如果……还要将青菜MM
和小邱分在不同的组最小代价是多少呢?……好麻烦啊,飞飞越想越乱,于是来找你帮忙了。一些说明:木木中学
有N个学生,木木中学是一所优秀的中学,学生们都是开朗外向的,所以一个学校里任意两个学生间接或者直接肯
定认识。显然任意一个辩论组都至少得有一个人。为了方便,我们把木木中学的学生用1到N编号,飞飞有Q个问题
,每个问题的形式都是这样的:将编号为a的学生与编号为b的学生分在不同组中,最少的代价是多少呢?关于这道
题目本身,只是想检验你能不能帮飞飞,所以你并不需要--具体回答每个问题,只需要最后输出每个问题答案的乘
积除以P的余数就可以了。
Input
输入文件中的第一行为四个正整数N,M,Q,P。
第二行至第M+1行,每行有三个正整数a,b,c,表示编号为a的同学和编号为b的同学认识,并且要好程度是c。
保证a和b是不会相同的,也不会有多行描述相同两个学生之间的关系。
第M+2行至第M+Q+1行,每行有两个正整数a,b,表示飞飞的一个问题,这里保证a和b是不同的。
N<5000,M<10000,Q<1000,C<1000000,P<1000000。
Output
输出文件中仅一行为一个整数,所有问题答案的乘积除以P的余数。
Sample Input
3 3 2 21
1 2 3
2 3 4
1 3 5
1 2
2 3
Sample Output
16
Sol
本来以为前两题都弃疗了第三题顶多抢救,结果读完了题目——这不就和关押罪犯一样的吗!!!然后就AC了。【三位数保底题】
思路很简单——读入每条边,从大到小排序,对于每个询问都跑一次最大生成树,如果遇到询问中的两个点在同一连通块里了,那么这条边就不能加进去,这条边就是这次询问的答案,交给ans累乘即可。其他操作和Kruskal几乎一样的。
#include<bits/stdc++.h>
#define maxn 5005
using namespace std;
typedef long long ll;
int read() {
int x = 0, ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x;
}
struct edge {
int u, v;
ll w;
bool operator < (const edge &x) const {return w > x.w;}//从大到小排序
}e[maxn << 2];
int n, m, q, mod, fa[maxn];
int get(int x) {return x == fa[x]? x : fa[x] = get(fa[x]);}
ll ans = 1;
ll mul(ll a, ll b) {//写了一个64位整数乘法……怕long long爆掉……
ll res = 0;
while(b) {
if(b & 1) res = (res + a) % mod;
b >>= 1;
a = (a << 1) % mod;
}
return res % mod;
}
int main() {
// freopen("group.in", "r", stdin);
// freopen("group.out", "w", stdout);
n = read(), m = read(), q = read(), mod = read();
for(int i = 1; i <= m; i++)
e[i].u = read(), e[i].v = read(), e[i].w = read();
sort(e + 1, e + 1 + m);
register int lu, lv, u, v;
for(int i = 1; i <= q; i++) {
lu = read(), lv = read();
for(int j = 1; j <= n; j++) fa[j] = j;//每次都要初始化
for(int j = 1; j <= m; j++) {
u = e[j].u, v = e[j].v;
u = get(u), v = get(v);
if(u == v) continue;.//已经在一起了,自动满足,跳过
fa[u] = v;
if(get(lu) == get(lv)) {ans = mul(ans, e[j].w); break;}//这条边不能加进去了
}
}
printf("%lld\n", ans);
return 0;
}
完了。
这次考试就这么水完了。
溜】