BUPT2017 springtraining(16) #3 ——搜索与动态规划
题目在这里啊
A.最长上升子序列,范围很小所以写了简单的O(n^2)算法
#include <iostream> #define rep(i, j, k) for(int i = j;i <= k;i ++) #define rev(i, j, k) for(int i = j;i >= k;i --) using namespace std; typedef long long ll; int n, m, a[], f[]; int main() {
ios::sync_with_stdio(false);
cin >> n;
rep(i, , n) cin >> a[i];
rep(i, , n) {
f[i] = ;
rep(j, , i - ) {
if(a[j] < a[i])
f[i] = max(f[i], f[j] + );
}
}
rep(i, , n) m = max(m, f[i]);
cout << m;
return ;
}
B.Sum( C[i] * G[i] ) = 0 的方案数嘛
就是个分组背包问题嘛,O(n^2 * m^2 * max_weight)
#include <iostream>
#include <cstdio> using namespace std; int n, m, x, y, s, t, a[], f[][]; int main() {
ios::sync_with_stdio(false);
cin >> n >> m, f[][] = ;
for(int i = ;i <= n;i ++)
cin >> a[i], a[i] += ;
for(int i = ;i <= m;i ++) {
cin >> x, s += x;
for(int j = ;j <= n;j ++) {
y = x * a[j];
for(int k = ;k >= y;k --) {
f[i][k] += f[i - ][k - y];
}
}
}
printf("%d\n", f[m][s * ]);
return ;
}
C.就是个阶乘+组合数,合在一起或者分开算都是ok的
不到30其实就能爆掉longlong了...题目保证不爆了就不管了
对,初始化请不要忘记有 k = 0
#include <bits/stdc++.h> #define rep(i, j, k) for(int i = j;i <= k;i ++) #define rev(i, j, k) for(int i = j;i >= k;i --) using namespace std; typedef long long ll; ll b[], a[][]; int t, n, k; int main() {
ios::sync_with_stdio(false);
a[][] = b[] = b[] = ;
rep(i, , ) a[i][] = ;
rep(i, , ) {
b[i] = b[i - ] * i;
rep(j, , i)
a[i][j] = a[i - ][j - ] + a[i - ][j];
}
cin >> t;
rep(i, , t) {
cout << "Case "<< i <<": ";
cin >> n >> k;
cout << a[n][k] * a[n][k] * b[k] << endl;
}
return ;
}
D.回文串好多都是区间DP
f[i][j]代表从 i 到j 这段区间能弄出多少种回文串
其实我的DP转移方程是对着样例数据YY出来的...
if(s[i] == s[j]) f[i][j] = f[i + 1][j] + f[i][j - 1] + 1
else f[i][j] = f[i + 1][j] + f[i][j - 1] - f[i + 1][j - 1]
因为不满足 s[i] == s[j] 的话,那样子加就会重复计算,所以需要减去重复部分
否则的话,因为这段区间两边字母相同
所以就可以在它们中间夹上f[i + 1][j - 1]种回文串(所以不用减重复
另外也可以什么都不加,所以还要 +1
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main() {
ios::sync_with_stdio(false);
int t;
ll f[][];
string s;
cin >> t;
for(int i = ;i <= t;i ++) {
cin >> s;
memset(f, , sizeof f);
for(int j = ;j < s.size();j ++) f[j][j] = ;
for(int d = ;d < s.size();d ++)
for(int j = ;j + d < s.size();j ++) {
int k = j + d;
if(s[j] == s[k]) f[j][k] = f[j + ][k] + f[j][k - ] + ;
else f[j][k] = f[j + ][k] + f[j][k - ] - f[j + ][k - ];
}
cout << "Case " << i << ": " << f[][s.size() - ] << endl;
}
return ;
}
E.我说Floyed你就会了吧,这就很有灵性!
#include <iostream>
#include <cstring>
#include <cstdio> #define rep(i, j, k) for(int i = j;i <= k;i ++) #define rev(i, j, k) for(int i = j;i >= k;i --) using namespace std; typedef long long ll; string s; bool f[][]; int main() {
ios::sync_with_stdio(false);
while(cin >> s) {
if(s[] == '') {
rep(k, , )
rep(i, , )
rep(j, , )
f[i][j] |= f[i][k] & f[k][j];
puts(f[][] ? "Yes." : "No.");
memset(f, , sizeof f);
}
else f[s[] - 'a'][*(s.end() - ) - 'a'] = ;
}
return ;
}
F.不存在任何一条路径上有两点颜色相同...
n*m的矩阵,路径固定长度为n + m - 1,所以n + m - 1 > k 就不存在方案
...所以n , m <= 1000完全开玩笑的
直观来看最坏情况就是在5 * 6的矩阵里
我们考虑爆搜+检验,O(10 ^ 30 * n * m)
1. 优化一下,压位来记录前 [ i * j ] 矩阵(不含a[i][j])里用过的颜色
来确定a[i][j]的可选颜色,然后这样大概在O(10 ^ 20)
2.再优化一下,如果前 [ i * j ] 矩阵(不含a[i][j])里用过的颜色数为 C
那么剩下部分(含a[i][j])最少需要颜色数 D = n - i + m - j - 1 + 2
如果 C + D > k 那么当前方案是不可能有解的, 这样大概 O(玄学)
当然仍过不了5 * 6 矩阵初始全空的情况
3.再优化一下,当前位置如果染色为 p 或 q
这两种颜色都是第一次被使用
即之前的dfs中染过色的以及初始就被染色的部分都没使用过这两种颜色
那么我们可以认为这两种颜色是等价的
换句话说就是当前位置染色为 p 再dfs下去
和当前位置染色为 q 再dfs下去对答案的贡献是一样的
所以可以只算一个,另一个直接加上就可以了
这时候效率O(玄学 --), 5 * 6 矩阵初始全空的情况已经能过了...
大力交一发能过了...15ms很快乐
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int Mod = 1e9 + ; int n, m, k, a[][]; int used[], g[][], f[][]; ll dfs(int x, int y) {
if(y > m) x ++, y = ;
if(x > n) return ;
int z = __builtin_popcount(g[x][y] = g[x - ][y] | g[x][y - ]);
if(z + n - x + m - y + > k) return ;
ll ret = , tmp = -;
if(!a[x][y]) {
for(int i = ;i <= k;i ++) {
if((g[x][y] | f[x][y]) & ( << i)) continue;
used[i] ++, g[x][y] |= ( << i);
if(used[i] == ) {
if(tmp == -) tmp = dfs(x, y + );
ret += tmp;
}
else {
ret += dfs(x, y + );
}
used[i] --, g[x][y] ^= ( << i);
}
}
else {
g[x][y] |= ( << a[x][y]);
ret = dfs(x, y + );
}
return ret % Mod;
} int main() {
ios::sync_with_stdio(false);
cin >> n >> m >> k;
if(n + m - > k) {
puts("");
return ;
}
for(int i = ;i <= n;i ++)
for(int j = ;j <= m;j ++) {
cin >> a[i][j];
if(a[i][j]) used[a[i][j]] ++;
}
for(int i = n;i;i --)
for(int j = m;j;j --) {
if(a[i][j] && ((f[i + ][j] | f[i][j + ]) & ( << a[i][j]))) {
puts("");
return ;
}
f[i][j] = f[i + ][j] | f[i][j + ] | ( << a[i][j]);
}
cout << dfs(, ) % Mod << endl;
return ;
}
G.结论题啦
3次bfs找到一条树的直径两个端点 s t
然后ans[i] = max( dis(i, s) , dis(i, t) )
#include <bits/stdc++.h> #define rep(i, j, k) for(int i = j;i < (k + 1);i ++) #define rev(i, j, k) for(int i = j;i >= k;i --) using namespace std; typedef long long ll; int n, st, en, vis[], dis[][]; vector <pair<int, int> > e[]; queue <int> q; void bfs(int s, int t) {
static int u;en = ;
memset(dis[t], , sizeof dis[t]);
memset(vis, -, sizeof vis);
q.push(s), vis[s] = t;
while(!q.empty()) {
u = q.front(), q.pop();
rep(i, , e[u].size() - )
if(t != vis[e[u][i].first] && dis[t][e[u][i].first] < dis[t][u] + e[u][i].second) {
dis[t][e[u][i].first] = dis[t][u] + e[u][i].second;
q.push(e[u][i].first), vis[e[u][i].first] = t;
}
}
rep(i, , n) if(dis[t][i] > en) en = dis[t][i], st = i;
} int main() {
int u, v;
ios::sync_with_stdio(false);
while(cin >> n) {
rep(i, , n) e[i].clear();
rep(i, , n) cin >> u >> v,
e[i].push_back(make_pair(u, v)), e[u].push_back(make_pair(i, v));
bfs(, ), bfs(st, ), bfs(st, );
rep(i, , n) cout << max(dis[][i], dis[][i]) << endl;
}
return ;
}
H.显然所有环都需要1次变成树
然后再数出树的数量,再并到一棵树上
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = ; vector <int> e[maxn]; int n, m, f[maxn], v[maxn], root[maxn]; void dfs1(int x) {
v[x] = ;
for(int i = ;i < e[x].size();i ++)
if(e[x][i] != x) dfs1(e[x][i]);
} int dfs2(int x, int t, int y = ) {
if(v[x] == t) return ;
else if(v[x] != && v[x] != t) return ;
v[x] = t;
for(int i = ;i < e[x].size();i ++)
y |= dfs2(e[x][i], t);
return y;
} int main() {
ios::sync_with_stdio(false);
cin >> n;
for(int i = ;i <= n;i ++) cin >> f[i], e[f[i]].push_back(i);
for(int i = ;i <= n;i ++)
if(f[i] == i)
dfs1(i), root[++ root[]] = i, m = ;
for(int i = ;i <= n;i ++)
if(!v[i] && dfs2(i, i))
root[++ root[]] = i;
if(m) {
cout << root[] - << endl;
for(int i = ;i <= root[];i ++)
f[root[i]] = root[];
for(int i = ;i <= n;i ++)
cout << f[i] << " ";
}
else {
cout << root[] << endl;
for(int i = ;i <= root[];i ++)
f[root[i]] = root[];
for(int i = ;i <= n;i ++)
cout << f[i] << " ";
}
return ;
}
I.转一下能不能转好,一共就只有12种转法...
除去顺时针和逆时针就是6种,枚举一下再验证吧
#include <iostream>
#include <cstdio> #define rep(i, j, k) for(int i = j;i <= k;i ++) using namespace std; int f[][] = {
, , , , , , , ,
, , , , , , , ,
, , , , , , , ,
, , , , , , , ,
, , , , , , , ,
, , , , , , ,
}; bool judge(int *a) {
for(int i = ;i < ;i += )
if(!(a[i] == a[i + ] && a[i] == a[i + ] && a[i] == a[i + ]))
return ;
return ;
} bool ok(int *a, int *b, int ret = ) {
static int c[];
rep(i, , ) c[i] = c[i + ] = a[b[i]];
rep(i, , ) a[b[i]] = c[i + ];
ret |= judge(a);
rep(i, , ) a[b[i]] = c[i + ];
ret |= judge(a);
rep(i, , ) a[b[i]] = c[i];
return ret;
} int main() {
ios::sync_with_stdio(false);
int n, m, a[];
cin >> n;
rep(i, , n) {
rep(i, , ) cin >> a[i];m = judge(a);
rep(i, , ) if(ok(a, f[i])) m = ;
puts(m ? "YES" : "NO");
}
return ;
}
J.一个简单的剪枝暴搜...
因为要满足同一行同一列同一区域只出现一次...
所以预处理一下就行了,压不压位随意吧...
WA了1h发现是dfs函数最后忘记写return 0...
很多地方会默认return 1所以不报编译错误...
这个时候vs大法就很舒服了...
虽然给你带个安全套,但是提醒肯定会提醒的!
#include <iostream>
#include <algorithm> using namespace std; int k, a[][], b[], c[], d[]; pair<int, int> p[]; bool dfs(int n) {
if(n > k) {
for(int i = ;i < ;i ++) {
for(int j = ;j < ;j ++)
printf("%d ", a[i][j]);
printf("%d\n", a[i][]);
}
return ;
}
int x = p[n].first, y = p[n].second, z = x / * + y / ;
for(int i = ;i <= ;i ++) {
if((b[x] | c[y] | d[z]) & ( << i)) continue;
a[x][y] = i;
b[x] ^= << i;
c[y] ^= << i;
d[z] ^= << i;
if(dfs(n + )) return ;
b[x] ^= << i;
c[y] ^= << i;
d[z] ^= << i;
}
return ;
} int main() {
ios::sync_with_stdio(false);
int i, j, flag = ;
char str[];
while(cin >> str) {
if(flag ++) puts("");
for(i = ;i < ;i ++) b[i] = c[i] = d[i] = ;
i = , j = , k = ;
if(str[] == '?') a[i][j] = -, p[++ k] = make_pair(i, j);
else a[i][j] = str[] - '', b[] |= << a[i][j], c[] |= << a[i][j], d[] |= << a[i][j];
for(j ++;i < ;i ++) {
for(;j < ;j ++) {
cin >> str;
if(str[] == '?') a[i][j] = -, p[++ k] = make_pair(i, j);
else a[i][j] = str[] - '', b[i] |= << a[i][j], c[j] |= << a[i][j], d[i / * + j / ] |= << a[i][j];
}
j = ;
}
dfs();
}
return ;
}
题外话:
日常被自己的ios::sync_with_stdio坑...
会取消cin和scanf,cout和printf的同步
所以一旦开了这玩意儿就绝对不能混用了!
当然开了这玩意儿后,cin几乎是绝对比scanf好用的
因为我们很少需要格式化读入的骚操作
而cout和printf各有利弊吧
简单输出cout仍然是比printf方便的
而格式化输出的话,printf更舒服
至于puts似乎是跟printf一路的吧...
不好好训练脑子就要锈死了...
BUPT2017 springtraining(16) #3 ——搜索与动态规划的更多相关文章
- BUPT2017 springtraining(16) #1 题解
https://vjudge.net/contest/162590 A: 不难发现,当L=R时输出L,当L<R时输出2. B: 贪心得配对.1和n配 2和n-1配,对与对直接只要花1个代价就可以 ...
- BUPT2017 springtraining(16) #6 ——图论
题目链接 A.容易发现最后字符的对应都是一对一的 或者说我们没办法出现最后多对一或者一对多的情况 所以只要算出 ‘a’ - 'z' 每个字符最后对应的字符即可 #include <cstdio& ...
- BUPT2017 springtraining(16) #4 ——基础数论
题目在这里 A.手动打表找规律得组合数 n -= 2, m -= 2, ans = C(n, m) #include <bits/stdc++.h> using namespace std ...
- BUPT2017 springtraining(16) #2 ——基础数据结构
题目在这里 A.似乎是个并查集+??? B.10W的范围,似乎可以暴力来一发二分+sort? 但我猜正解可以O(nlogn)? C.单调队列入门题目 #include <cstdio> ] ...
- BUPT2017 springtraining(16) #1 ——近期codeforces简单题目回顾
这里是contest 8道题全部来源于 cf 的两场contest (出题人可真懒啊 Codeforces Round #411 (Div. 2)的ABCDE Codeforces Round #40 ...
- 【BZOJ2246】[SDOI2011]迷宫探险(搜索,动态规划)
[BZOJ2246][SDOI2011]迷宫探险(搜索,动态规划) 题面 BZOJ 洛谷 题解 乍一看似乎是可以求出每个东西是陷阱的概率,然而会发现前面走过的陷阱是不是陷阱实际上是会对当前状态产生影响 ...
- Luogu 2540 斗地主增强版(搜索,动态规划)
Luogu 2540 斗地主增强版(搜索,动态规划) Description 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来进行的扑克牌游 ...
- Luogu 2668 NOIP 2015 斗地主(搜索,动态规划)
Luogu 2668 NOIP 2015 斗地主(搜索,动态规划) Description 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来 ...
- Luogu 1514 引水入城 (搜索,动态规划)
Luogu 1514 引水入城 (搜索,动态规划) Description 在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠.该国的行政区划十分特殊,刚好构成一个N行M列的矩形,如上图 ...
随机推荐
- 基于Hive的手机应用信息统计分析系统
目录 项目概要 具体实现 HIVE查询 项目概要 需求 手机应用日志 定期离线分析手机应用新增用户.活跃用户.沉默用户.启动次数.版本分布和留存用户等业务指标. 工作流程 手机APP启动时,上报启动日 ...
- PID204特种部队
特种部队 题目描述 Description 某特种部队接到一个任务,需要潜入一个仓库.该部队士兵分为两路,第一路士兵已经在正面牵制住了敌人,第二路士兵正在悄悄地从后方秘密潜入敌人的仓库.当他们到达 ...
- Python细节(一)深浅拷贝
深浅拷贝 只要涉及拷贝,就会涉及创建新对象 浅拷贝,会创建一个新的容器,列表中的元素和原列表的元素用的是同一个内存空间 第一种方法:从头切到尾,完整的复制一份 lst = [1,2,3,4] lst1 ...
- assistant: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by
[oracle@oracledb button]$ assistantassistant: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' no ...
- Java系列学习(十一)-内部类
1.内部类 (1)把类定义在另一个类的内部,该类就称为内部类 (2)内部类的访问规则 A:内部类可以直接访问外部类的成员,包括私有 B:外部类要想访问内部类的成员,必须创建对象 (3)内部类的分类 A ...
- SQL之T-sql 语句操作数据库
用SQL语句操作数据库 在上一次的话题中我们谈到了怎么使用数据库,说到了数据库的基本用法. 不过只是仅限于一些简单的操作,so 如果你不想被人说--"你们只是动动鼠标操作就可以了! 没什么技 ...
- jQuery中国各个省份地图分部代码
jQuery中国各个省份地图分部代码 在线演示本地下载
- window.showModalDialog的问题
通过window.showModalDialog的方式弹出B页面,总报“拒绝访问”的错误,将站点添加到受信任站点可以解决这个问题
- 初学者Android studio安装
学习过java基础,最近趁着大量课余时间想学习Android开发.百度很多资料Android studio,由Google开发的开发工具,那就不需要再多说.对于初学者的我来说,一定足够用了.此文主要介 ...
- TCP的send与recv函数小结
Send函数: 在阻塞模式下, send函数的过程是将应用程序请求发送的数据拷贝到发送缓存中发送并得到确认后再返回.但由于发送缓存的存在,表现为:如果发送缓存大小比请求发送的大小要大,那么send函数 ...