2018/5/24模拟赛总结
shzr带病AK虐爆全场...
T1n皇后:
这题没啥好说的...
T2有重复元素的排列问题:
【问题描述】
设R={ r1, r2 , …, rn}是要进行排列的n个元素。其中元素r1, r2 , …, rn可能相同。试设计一个算法,列出R的所有不同排列。
【编程任务】
给定n 以及待排列的n 个元素。计算出这n 个元素的所有不同排列。
【输入格式】
由perm.in输入数据。文件的第1 行是元素个数n,1≤n≤500。接下来的1 行是待排列的n个元素。
【输出格式】
计算出的n个元素的所有不同排列输出到文件perm.out中。文件最后1行中的数是排列总数。
出锅出在T2上,T掉了7个点...
考试时的想法是:生成r[1]~r[n]的全排列,之后用Hash+map去重(比只用map要快一(很)些(多))
然而还是T掉了..仔细一看数据范围\(n≤500\),全排列\(n!\)的复杂度能给到三十分就已经是仁慈了...
正解应该是用一个桶,记录每个字母的个数,然后枚举26个字母的全排列就好了..
正解代码如下:
#include<iostream>
#include<cstdio>
using namespace std;
int n,f[501],a[501],ans;
char r[501];
void search(int t) {
int k;
if(t>n) {
ans++;
for(k=1;k<=n;k++) printf("%c",a[k]+96);
printf("\n");
return;
}
for(k=1;k<=26;k++)
if(f[k]>0) {
a[t]=k; f[k]--;
search(t+1);
f[k]++;
}
}
int main() {
scanf("%d",&n);
cin>>r;
for(int i=0;i<n;i++) f[r[i]-96]++;
search(1);
printf("%d",ans);
return 0;
}
但wzx同学发现了一种神奇做法:stl里的next_permutation
实测开\(O_2\) 8ms,不过正解200多ms是smg..,stl大法好!
代码如下:
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define re register
using namespace std;
int n, tot;
char ch[500 + 1];
int main() {
scanf("%d\n%s", &n, ch);
sort(ch, ch + n);
do {
puts(ch);
++tot;
}while(next_permutation(ch, ch + n));
printf("%d", tot);
return 0;
}
T3装载问题:
【问题描述】
有一批共n个集装箱要装上艘载重量为c的轮船,其中集装箱i的重量为wi。找出一种最优装载方案,将轮船尽可能装满,即在装载体积不受限制的情况下,将尽可能重的集装箱装上轮船。
【输入格式】
由文件load.in给出输入数据。第一行有2个正整数n和c。n是集装箱数,c是轮船的载重量。接下来的1行中有n个正整数,表示集装箱的重量。
【输出格式】
将计算出的最大装载重量输出到文件load.out。
【输入样例】
5 10
7 2 6 5 4
【输出样例】
10
一看,这明显是01背包啊!loli太毒瘤了..明明说是只考搜索的模拟赛,用搜索的这道题都T掉了...
可以把货物的重量看做价值(因为要求重量最大嘛..),然后跑一个sb背包就好.
闲得无聊打了一个暴力,被极限数据卡成狗...
T4字符序列(characts)
【问题描述】
从三个元素的集合[A,B,C]中选取元素生成一个N个字符组成的序列,使得没有两个相邻字的子序列(子序列长度=2)相同。例:N = 5时ABCBA是合格的,而序列ABCBC与ABABC是不合格的,因为其中子序列BC,AB是相同的。
对于由键盘输入的N(1<=N<=12),求出满足条件的N个字符的所有序列和其总数。
一开始以为长度不定,两个区间的位置没有限制,check函数写了一个三重循环的(把四重优化了一下)
然后发现不对啊!搜索里面这么check不超时才鬼了嘞
开始仔细读题:
-
子序列长度=2
-
两个相邻字的子序列
一下子check的复杂度就变成O(n)了...
打了打搜索发现\(n=12\)的数据好像要1s多才能跑出来..
算了,不剪枝了,打一个12的表就好了
loli出题的时候好像没有考虑到可打表的问题.
我的打表程序比正解还短233,毕竟只有12的大小..
T5图的m着色问题color
【问题描述】
给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。如果有一种着色法使G中每条边的2个顶点着不同颜色,则称这个图是m可着色的。图的m着色问题是对于给定图G和m种颜色,找出所有不同的着色法。
【编程任务】
对于给定的无向连通图G和m种不同的颜色,编程计算图的所有不同的着色法。
【输入格式】
文件color.in输入数据。第1行有3个正整数n,k 和m,表示给定的图G有n个顶点和k条边,m种颜色。顶点编号为1,2,…,n。接下来的k行中,每行有2个正整数u,v,表示图G 的一条边(u,v)。
【输出格式】
程序运行结束时,将计算出的不同的着色方案数输出到文件color.out中。
当时用邻接表(又是一道图论题,毒瘤啊)没有打出来..就用的邻接矩阵,生成颜色的全排列再\(n^2\)遍历一遍判断
T了三个点(三个点卡卡时就过了..满地打滚求O3优化...)
考完以后用邻接表打了一遍,代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#define re register
using namespace std;
const int MAXN = 100 + 5;
const int MAXM = MAXN * (MAXN - 1) / 2;
int n, k, m;
int tot, c[MAXN];
bool vis[MAXN];
int edge_num, head[MAXN];
struct Edge {
int to, nxt;
}h[MAXM << 1];
inline int read() {
int x=0; char ch = getchar();
while(ch<'0' || ch>'9') ch = getchar();
while(ch>='0' && ch<='9')
x=(x<<3)+(x<<1)+ch-48, ch=getchar();
return x;
}
inline void Add(int from, int to) {
h[++edge_num].nxt = head[from];
h[edge_num].to = to;
head[from] = edge_num;
}
void dfs(int u) {
if(u == n) {
++tot;
return;
}
for(re int i=1; i<=m; ++i) {
bool fl = 1;
for(re int j=head[u+1]; j; j=h[j].nxt)
if(c[h[j].to] == i) {
fl = 0;
break;
}
if(!fl) continue;
c[u + 1] = i;
dfs(u + 1);
c[u + 1] = 0;
}
}
int main() {
n = read(), k = read(), m = read();
for(re int i=1; i<=k; ++i) {
int x = read(), y = read();
Add(x, y), Add(y, x);
}
dfs(0);
printf("%d\n", tot);
return 0;
}
这次模拟赛其实好多人都能AK的,但只有一个人AK...
大家都交卷交的太早了...