文章目录
4.26比赛(枚举+DFS)
题目大意:给出12道题做出的概率,问做出0-12道题目的概率
/*
4.26 比赛 :https://ac.nowcoder.com/acm/problem/14734
对于每一个题目要么做出,要么做不出,利用dfs进行判断即可
// 还有一种dp做法,未完待续......
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 15;
double a[N], b[N], c[N], un[N];
double ans;
void dfs(int pos, int now, int need, double p)
{
if(pos == 13) // 当所有题目都写完了
{
if(now == need) { // 达到了需要
ans += p;
}
return ;
} // 判断该题做不做出来
if(now < need )
dfs(pos + 1, now + 1, need, p * (1 - un[pos]));
dfs(pos + 1, now, need, p * un[pos]);
}
int main()
{
for (int i = 1; i <= 12; ++i) cin >> a[i];
for (int i = 1; i <= 12; ++i) cin >> b[i];
for (int i = 1; i <= 12; ++i) cin >> c[i];
// 一道题做不出来:自己做不出,没听到左边也没听到右边
for (int i = 1; i <= 12; ++i) un[i] = (1-a[i]) * (1-b[i]) * (1-c[i]);
for (int i = 1; i <= 13; ++i)
{
ans = 0;
dfs(1, 0, i - 1, 1);
printf("%.6f\n", ans);
}
return 0;
}
4.27滑雪与时间胶囊(BFS+Kruskal)
思路:
最小生成树
首先利用BFS求出连通图,然后用Kruskal算法求出最小树
// 未完待续:Prim
代码:
/*
Problem : 给出n个景点,m个边连接各个景点,求出最多景点的最短距离
Solution :BFS求连通图,Kruskal求最小生成树
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
// 快读模板
inline int read()
{
int s = 0, w = 1; char ch = getchar();
while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return s * w;
}
// 数据范围
const int Max = 1e6 + 10;
int n, m, h[Max];
int head[Max], tot; // 链式前向星存图;
struct node{
int u, v, w, next;
bool operator < (const node& b)const{
if(h[v] == h[b.v]) return w < b.w;//高度相同的时候,边权升序
return h[v] > h[b.v]; // 高度降序
}
}edge[Max<<1];
int vis[Max], p[Max]; // 记录结点是否在图中。并查集数组
ll cnt, sum; // 结点个数及权值
void add(int u, int v, int w) // 增加一条u->v边权为w的边
{
tot++;
edge[tot].u = u;
edge[tot].v = v;
edge[tot].w = w;
edge[tot].next = head[u];
head[u] = tot;
}
//并查集find函数
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
void BFS(int x) // BFS求出连通图
{
queue<int>Q;
Q.push(x);
vis[x] = 1;
cnt ++;
while(Q.size())
{
int u = Q.front();
Q.pop();
for (int i = head[u]; i ; i = edge[i].next)
{
int v = edge[i].v;
if(!vis[v]) // 没用过
{
Q.push(v);
cnt ++;
vis[v] = 1;
}
}
}
}
void Kruskal() // 求出最小生成树
{
sum = 0;
for (int i = 1; i <= n; ++i) p[i] = i; // 初始化并查集
for (int i = 1; i <= tot; ++i)
{
int u = edge[i].u, v = edge[i].v, w = edge[i].w;
if(vis[u] && vis[v]) // 这两个点都在图中
{
if(find(u) != find(v)) // 并且不在一个集合里面
{
p[find(u)] = find(v); // 将这两个集合合并
sum += w; // 权值增加
}
}
}
}
int main()
{
n = read();
m = read();
memset(h, 0, sizeof h);
for (int i = 1; i <= n; ++i) h[i] = read();
int u, v, w;
memset(head, 0, sizeof head);
tot = 0;
for (int i = 1; i <= m; ++i)
{
u = read();
v = read();
w = read();
if(h[u] >= h[v]) add(u, v, w);
if(h[u] <= h[v]) add(v, u, w);
}
BFS(1);
sort(edge+1, edge+1+tot); // 高度降序,边权升序
Kruskal();
cout << cnt << ' ' << sum << endl;
return 0;
}
4.28储物点的距离(前缀和)
首先对距离进行前缀和预处理,然后需将每个点的数量进行前缀和处理,最后要另外开一个数组表示到1的花费
The most important tips: 减法的取模
a - b 对 c 取模
Answer : ( a - b + c ) % c;
- 然后就是对x的三种情况的分类讨论
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 200010, mod = 1e9 + 7;
typedef long long ll;
ll a[N], b[N], sum[N];
ll n, m;
ll x, l , r;
int main()
{
scanf("%lld%lld", &n, &m);
for (int i = 2; i <= n; ++i) scanf("%lld", &a[i]), a[i] = (a[i - 1] + a[i]) % mod;
for (int i = 1; i <= n; ++i){
scanf("%lld", &b[i]);
sum[i] = (sum[i - 1] + a[i] * b[i]) % mod;
b[i] = (b[i - 1] + b[i] ) % mod;
}
ll ans;
while( m -- )
{
ans = 0;
scanf("%lld%lld%lld", &x, &l, &r);
if(x <= l)
{
ans = (sum[r] - sum[l - 1] + mod) % mod;
ans = (ans - ((b[r] - b[l - 1] + mod) % mod * a[x] % mod) + mod) % mod;
}
else if(r <= x)
{
ans = ((b[r] - b[l - 1] + mod ) % mod * a[x] % mod) % mod;
ans = (ans - ((sum[r] - sum[l - 1 ]) + mod) % mod + mod)% mod;
}
else
{
// [l, x] + [x, r]
ans = (sum[r] - sum[x - 1] + mod) % mod;
ans = (ans - ((b[r] - b[x - 1] + mod) % mod * a[x] % mod) + mod) % mod;
ans += ((b[x] - b[l - 1] + mod) % mod * a[x] % mod ) % mod;
ans = (ans - ((sum[x] - sum[l - 1]) + mod) % mod + mod) % mod;
}
cout << ans << endl;
}
return 0;
}
4.29中位数图(思维)
思路: 找出b的位置,并且把大于b的标记为1,小于1的标记为-1,然后再pos位置往两边判断,当sum为的时候表示有一个答案,然而对于两边的情况,利用一个数组记录左边的情况,当右边有与之对应的时候,答案加一
代码:
/*
Solution:对于序列中位数,大于他的数一定等于小于他的数
ps:单独的自己也算
*/
#include<bits/stdc++.h>
using namespace std;
const int Max = 100010;
int n, b, a[Max], num[Max<<1]; // a[i] 原数组,num[i] 表示一边的情况
int main()
{
scanf("%d%d", &n, &b);
int pos, x;
for (int i = 1; i <= n; ++i) // 将大于标记为1, 小于标记为-1,
{ // 并把b的位置pos找出来
scanf("%d", &x);
if(x > b) a[i] = 1;
else if(x < b) a[i] = -1;
else pos = i;
}
int sum = 0, ans = 1; // 对pos的左边进行寻找,并且记录左边的大小情况
for (int i = pos - 1; i >= 1; --i)
{
sum += a[i];
num[n - sum] ++;
if(!sum) ans ++;
}
sum = 0; // 将sum初始化,开始右边的判断,如果在左边有相对应的的大小情况答案加一
for (int i = pos + 1; i <= n; ++i)
{
sum += a[i];
ans += num[n + sum];
if(!sum) ans ++;
}
cout << ans << endl;
return 0;
}
4.30codeJan与旅行(暴力+思维)
思路: 要在n座城市中访问m次,求最短路径,答案一定是在某两个城市之间一直来回,
假设从p走到第i座城市,然后是在i与i+1座城市之间来回答案为: abs(p-a[i]+num*(a[i+1]-a[i])
有特殊情况为,可以先走另外一边然后在执行前面的方案,答案为:
abs(p-a[i]) + (num-1)*(a[i+1]-a[i]) + abs(a[pos] - p)*2;
Hack数据:
1
3 10 2
1 10 14
AC代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll a[100010];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while(t -- )
{
int n, m, p;
cin >> n >> m >> p;
int pos = 0;
for (int i = 1; i <= n; ++i){
cin >> a[i];
if(a[i] < p) pos = i; // 接近p的城市
}
ll ans = 1e18;
for (int i = 1; i < n; ++i)
{
if(i <= pos){ // 左边
if(pos - i + 1 > m) continue;
ll num = m - (pos - i + 1), dist = p - a[i];
ans = min(ans, dist + num * (a[i+1] - a[i]));
if(num > 0 && pos + 1 <= n)
ans = min(ans, (a[pos+1] - p) * 2 + dist + (num-1)*(a[i+1]-a[i]));
}
else {
if(i - pos > m) continue;
ll num = m - (i - pos), dist = a[i] - p;
ans = min(ans, dist + num * (a[i+1]-a[i]));
if(num > 0 && pos >= 1)
ans = min(ans, (p - a[pos])*2 + dist + (num-1)*(a[i+1]-a[i]));
}
}
cout << ans << endl;
}
return 0;
}