这几天主要在写robocom的题,写了个初赛就自闭了,我怎么可以这么菜啊。
7-1 懂的都懂(20分)
题意:设n个非负整数和k次询问,每一次询问中的m个数字是否都是n个非负整数中其中四个数字的平均值,是的话输出Yes,否则输出No。
题目特征:n个数中选4个数,并且要对这个结果进行处理。
思路:dfs,搜到四个数就停下来,把平均值用map存起来,记得要回溯。我写代码的时候采用的是固定一个数,然后去搜剩下的三个数,但其实搜四个数也是一样的。(第一反应居然是全排列,蓝桥杯打傻了
1. #include<bits/stdc++.h>
2. using namespace std;
3. int n, k, a[55], vis[55];
4. map<int, int> mp;
5. void dfs(int num, int sum, int kk){
6. if(num == 4){ //选够四个数
7. if(sum % 4 == 0) mp[sum / 4] = 1; //因为输进来的都是整数,所以确定模4等于0之后才能存入
8. return;
9. }
10. vis[kk] = 1;
11. for(int i = 0; i < n; i++){
12. if(!vis[i]){
13. dfs(num + 1, sum + a[i], i);
14. }
15. }
16. vis[kk] = 0; //回溯
17. }
18. int main(){
19. ios::sync_with_stdio(false);
20. cin.tie(0);
21. cin >> n >> k;
22. int kk = 0;
23. for(int i = 0; i < n; i++){
24. cin >> a[i];
25. }
26. sort(a, a + n);
27. for(int i = 0; i < n; i++){
28. dfs(1, a[i], i);
29. }
30. // for(auto it = mp.begin(); it != mp.end(); it++){
31. // printf("****%d\n", (*it).first);
32. // }
33. for(int i = 0; i < k; i++){
34. int flag = 0;
35. int x; cin >> x;
36. for(int j = 0; j < x; j++){
37. int g; cin >> g;
38. if(!mp[g]) flag = 1;
39. }
40. if(!flag) printf("Yes\n");
41. else printf("No\n");
42. }
43. return 0;
44. }
题意:假设现在有 N 个机场,M 条航线,每天都会新增一个机场无法正常运作,每天会有 Q i对旅客从 X i机场前往 Y i机场,请计算有多少对旅客会受到影响无法完成行程。
思路:并查集+反向建图,最开始写的是dfs,只过了部分测试点,看了题解之后直呼妙啊,先把所有变量存下来,然后从后面的点开始建图,因为点是从前向后一个一个消失的,建立一个点相当于该点的前一个点消失后的状态(太妙了。在此之前还要对没有被防控的机场建图。
代码:
#include<bits/stdc++.h>
using namespace std;
struct node{
int st, ed;
};
int fa[50005], vis[50005];
int n, m, d;
vector<vector<node> > v(50005);
vector<node> vn;
vector<int> ans;
stack<int> sta;
void init(){
for(int i = 0; i <= n; i++) fa[i] = i;
}
int find(int x){
if(x == fa[x]) return x;
else return fa[x] = find(fa[x]);
}
int main(){
cin >> n >> m >> d;
init();
for(int i = 0; i < m; i++){
int a, b; cin >> a >> b;
vn.push_back({a, b});
}
for(int i = 0; i < d; i++){
int c, q; cin >> c >> q;
sta.push(c);
vis[c] = 1;
for(int j = 0; j < q; j++){
int x, y; cin >> x >> y;
v[c].push_back({x, y});
}
}
for(int i = 0; i < m; i++){
if(vis[vn[i].st] || vis[vn[i].ed]) continue;
int finda = find(vn[i].st);
int findb = find(vn[i].ed);
if(finda != findb){
fa[finda] = findb;
}
}
while(!sta.empty()){
int qf = sta.top(), cnt = 0;
sta.pop();
for(int i = 1; i <= n; i++) int ww = find(i);
for(int i = 0; i < v[qf].size(); i++){
if((fa[v[qf][i].st] != fa[v[qf][i].ed]) || vis[v[qf][i].st] || vis[v[qf][i].ed]) cnt++;
}
for(int i = 0; i < m; i++){
if(vn[i].st != qf && vn[i].ed != qf) continue;
int finda = find(vn[i].st);
int findb = find(vn[i].ed);
if(finda != findb){
fa[finda] = findb;
}
}
vis[qf] = 0;
ans.insert(ans.begin(), cnt);
}
for(int i = 0; i < ans.size(); i++){
printf("%d\n", ans[i]);
}
}
7-3 打怪升级
这一题没有全部过完(23/25)不知道是哪里出了问题,看好多人都是用迪杰斯特拉去路径还原,而我搜了一个弗洛伊德的路径还原板子,照着写上去了,但是没有全过。
题意:找一个点到输入的所有点耗费的能量最小,并且沿途获得的武器价值最大。
思路:典型的最短路问题,数据范围很小,考虑使用floyd,并且不仅要记录耗费的能量,还要记录获得的价值,路径能量耗费相同的情况下选择获得价值高的,还有最短路的路径还原问题。
路径还原参照博客:(36条消息) floyd——记录路径,原理详解。_小酒窝.的博客-优快云博客_floyd算法怎么记录路径
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
struct node{
int me, wv; //武器价值和怪兽能量
};
int pre[1005][1005]; //记录路径还原
int n, m;
node g[1005][1005]; //记录图
int num[1005]; //要攻破的堡垒
void print(int i,int j) //路径还原的输出
{
if(i==j) return ;
if(pre[i][j]==0) cout << "->" << j;
else{
print(i,pre[i][j]);
print(pre[i][j],j);
}
}
int main(){
cin >> n >> m;
memset(g, inf, sizeof(g));
memset(pre, 0, sizeof(pre));
for(int i = 0; i < m; i++){
int b1, b2, x, y; cin >> b1 >> b2 >> x >> y;
g[b1][b2] = g[b2][b1] = {x, y};
}
for(int k = 1; k <= n; k++){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(g[i][k].me + g[k][j].me < g[i][j].me)
{
pre[i][j]=k;
g[i][j].me = g[i][k].me + g[k][j].me;
g[i][j].wv = g[i][k].wv + g[k][j].wv;
}
else if(g[i][k].me + g[k][j].me == g[i][j].me){
if((g[i][k].wv + g[k][j].wv > g[i][j].wv) && (g[i][k].wv + g[k][j].wv < inf ))
{
pre[i][j]=k;
g[i][j].wv = g[i][k].wv + g[k][j].wv;
}
}
}
}
}
int k; cin>>k;
for(int i=0;i<k;i++) cin>>num[i];
int ans=inf;
int ans1;
for(int i=1;i<=n;i++)
{
int maxx=-1;
for(int j=0;j<k;j++)
{
int dis=g[i][num[j]].me;
maxx=max(maxx,dis);
}
if(ans>maxx)
{
ans=maxx;
ans1=i;
}
}
cout<<ans1<<endl;
for(int i = 0; i < k; i++){
printf("%d", ans1);
print(ans1,num[i]);
printf("\n");
if(ans1 == num[i]) printf("0 0\n");
else printf("%d %d\n", g[ans1][num[i]].me, g[ans1][num[i]].wv);
}
}
7-2芬兰木棋(25分)
芬兰木棋(Mölkky,又称芬兰木柱)是源自芬兰的一项运动。哲哲将这个运动改造成了赛博朋克单人版,现在场上一开始有 N 根立起的小木棋(上面分别标有一个非负整数),哲哲投掷一根大木棋去击倒这些小木棋以获得分数。分数规则如下:
- 如果仅击倒 1 根木棋,则得木棋上的分数。
- 如果击倒 2 根或以上的木棋,则只得击倒根数的分数。(例如击倒 5 根,则得 5 分。)
哲哲固定站在 (0,0) 点上,四周放着若干个小木棋 (Xi,Yi),坐标均为整数。每次哲哲可以朝一个方向扔出大木棋,大木棋会打倒这个方向上离哲哲最近的 k 个小木棋。哲哲游戏水平很高超,所以这个 k 可以自由控制。
请问哲哲最多能拿多少分,在获得最多分数的情况下最少需要扔出多少次大木棋?
规则与真实规则有较大出入,真实游玩时请以国际莫尔基组织的规则为准
输入格式:
输入第一行是一个正整数 N (1 ≤ N ≤ 105),表示场上一开始有 N 个木棋。
接下来 N 行,每行 3 个整数 Xi,Yi,Pi,分别表示木棋放置在 (Xi,Yi),木棋上的分数是 Pi。坐标在 32 位整数范围内,分数为小于等于 1000 的正整数。
保证 (0,0) 点没有木棋,也没有木棋重叠放置。
输出格式:
输出一行两个数,表示最多分数以及获得最多分数最少需要投掷大木棋多少次。
输入样例:
11
1 2 2
2 4 3
3 6 4
-1 2 2
-2 4 3
-3 6 4
-1 -2 1
-2 -4 1
-3 -6 1
-4 -8 2
2 -1 999
输出样例:
1022 9
题意:如题,这道题题意还是比较好理解的。
思路:计算斜率,对斜率相同的点存进一个容器里,然后计算有多少个连续的1,若不是1,那就直接加上去,这里要注意的是,斜率相同,有可能象限不同,所以还要分象限,我对每个容器都加了个0,0进行隔断。
今天学到了个额外的东西,就是自定义结构体内嵌比较器一定要写清楚,清楚到足以区分每个结构体,因为有些容器的排序是完全看你的自定义结构体内嵌比较器,如果不能够区分清楚,那电脑就会认为它们是同一个东西。
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct node{
int x, y, p;
};
bool cmp(node a, node b){
if(a.x != b.x) return a.x < b.x;
else return a.y < b.y;
}
signed main(){
int n, ans1 = 0, ans2 = 0; cin >> n;
map<double, vector<node> > mp;
for(int i = 0; i < n; i++){
int xx, yy, pp; cin >> xx >> yy >> pp;
ans1 += pp;
if(mp[xx * 1.0 / yy].size() == 0){
node a;
a.x = 0, a.y = 0, a.p = 0;
mp[xx * 1.0 / yy].push_back(a);
}
node b;
b.x = xx, b.y = yy, b.p = pp;
mp[xx * 1.0 / yy].push_back(b);
}
for(auto it = mp.begin(); it != mp.end(); it++){
vector<node> v = (*it).second;
sort(v.begin(), v.end(), cmp);
int index = -1;
for(int i = 0; i < v.size(); i++){
if(v[i].p == 0) continue;
else if(v[i].p != 1) ans2++;
else{
if(i == v.size() - 1 || v[i + 1].p != 1) ans2++;
}
}
}
printf("%lld %lld\n", ans1, ans2);
}