2020年首届算法竞赛网络挑战赛直播讲解课程
比賽鏈接
菜雞的我,第四名。。
A 矛盾激化
題意
給定地圖,這個地圖有兩個出口,現在我們需要求出從所有點到任意一個出口的距離中的最短路徑的最大值
本題為輸出答案題,給定你一種情況,然后輸出它的答案
題解
如果實在不會,可以手查(滑稽)
如果直接從每個點開始搜一遍最短距離不現實
因為只有兩個出口,所以我們可以從出口出發然后跑兩邊BFS,每個點都維護一個距離的最小值
有幾個坑點:
1.從出口往里面跳的時候只能跳一個格子
2.從別的格子跳往其他格子時只能跳兩個格子
3.跳兩格的時候還要判斷中間隔過去的一格是不是空格
4.輸入的格子有些惡心人
代碼
#include <iostream> #include <cstdio> #include <queue> #include <cstring>const int INF = 2147483647;using namespace std;int n, m, sx[3], sy[3], cnt, ans; char map[208][90]; int dis[208][90]; int dx[5] = {0, 2, 0, -2}; int dy[5] = {2, 0, -2, 0};int rx[5] = {0, 1, 0, -1}; int ry[5] = {1, 0, -1, 0}; struct node {int x, y, sum; }temp, now; queue<node> Q; bool vis[208][90];inline void BFS(int num) {memset(vis, 0, sizeof(vis));while (!Q.empty()) Q.pop();vis[sx[num]][sy[num]] = 1;temp.sum = 0, temp.x = sx[num], temp.y = sy[num];Q.push(temp);while (!Q.empty()) {now = Q.front();int x = now.x, y = now.y, s = now.sum;Q.pop();dis[x][y] = min(s, dis[x][y]);for(int i=0; i<4; i++) {int xx = dx[i]+x, yy = dy[i]+y;//走兩格 int zx = rx[i]+x, zy = ry[i]+y;//走一格 if(!vis[xx][yy] && map[xx][yy] == ' ' && xx <= 2*n+1 && xx > 0 && yy <= 2*m+1 && yy > 0 && map[zx][zy] == ' ' && (x != sx[num] || y != sy[num])) {//普通跳兩格 vis[xx][yy] = 1;temp.x = xx, temp.y = yy, temp.sum = s+1;Q.push(temp);}if(!vis[zx][zy] && map[zx][zy] == ' ' && x == sx[num] && y == sy[num] && zx <= 2*n+1 && zy <= 2*m+1 && zx > 0 && zy > 0) {//當從出口往里跳 vis[zx][zy] = 1;temp.x = zx, temp.y = zy, temp.sum = s+1;Q.push(temp);}}} }int main() {scanf("%d%d", &m, &n);for(int i=1; i<=2*n+1; i++) {for(int j=1; j<=2*m+1; j++) {dis[i][j] = INF;}}gets(map[0]);for(int i=1; i<=2*n+1; i++) {gets(map[i]+1);for(int j=1; j<=2*m+1; j++) {if(i == 1 || j == 1 || i == 2*n+1 || j == 2*m+1)//邊界 if(map[i][j] == ' ') {//記錄出口 sx[++cnt] = i;sy[cnt] = j;}}}BFS(1);BFS(2);//從兩個出口開始bfs for(int i=1; i<=2*n+1; i++) {for(int j=1; j<=2*m+1; j++) {if(dis[i][j] != INF)ans = max(ans, dis[i][j]);}}printf("%d", ans); }B 我去前面探探路
題意
對于一個樹,我們可以進行兩種操作,第一種:選擇一個節點,與該節點相連的邊會被標記。第二種:選擇第一個節點,與該節點相連的邊會被標記,同時與該節點相連的節點的相連的邊也會被標記。這兩種操作有各自的花費。問將所有邊標記所需花費是多少
題解
樹形dp
我們可以發現,在兒子節點操作2,與自己操作1對父親節點的影響一樣的
也就是這兩種情況在后效性上是等價的
也就是他自己其實也可以被對兄弟操作2給影響
我們可以得到:
dp[u][0] :以點u為根的子樹下的邊全部被覆蓋,且沒有向u節點上方覆蓋。也就是u不放,u的子節點只能進行操作1
dp[u][1]:以點u為根的子樹下的邊全部覆蓋,且向上覆蓋長度為1。u進行操作1,或者u為空,u的子節點進行操作2
dp[u][2]:以點u為根的子樹下的邊全部覆蓋,且向上覆蓋長度為2。u進行操作2
dp[u][3]:以點u為根的子樹的子樹里的邊都被覆蓋,但是u和子樹間的邊不一定被覆蓋。也就是父親節點進行操作2
v是u的兒子節點
dp[u][3]是為了處理 1-2-3-4-5-6-7-8-9,c1=10,c2=15這種極端的情況,只要c2覆蓋3和7,就能覆蓋所有的邊
dp[u][j]是不同情況 覆蓋以u為節點的子樹的最小代價
int t=min(dp[v][0],min(dp[v][1],dp[v][2]));dp[u][0]+=dp[v][1]; //u的子節點v是1,u才能是0(向上覆蓋長度0),不能有2,否則就會向上覆蓋dp[u][1]+=t; //向上覆蓋長度是1,有兩種選擇: 1.在u上放置c1,v上1,2,3隨意 2.u上不放,在一個v上放置c2,然后其他的v上隨意,dp[u][2]+=min(t,dp[v][3]); //u放置了c2,子節點v處于什么狀態(0,1,2,3)都行dp[u][3]+=t; //因為是狀態3,所以u和v之間的邊被它的父節點覆蓋,所以v不受上面影響,可以當作一個單獨的樹看待optimal=min(optimal,dp[v][2]-t); //其中一個v上放置c2,選擇增加費用最小的一個v代碼
#include<iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> using namespace std; const int maxn=10005,maxc=1005,INF=0x3f3f3f3f; int n,c1,c2,a,b,dp[maxn][4]; //dp[u][v],v=2,1,0,把放置的裝置看作勢,也就是v的值,然后勢向周圍逐層減小 // 當u上放置c2,v=2,u上放置c1或者離c2一跳是v=1,當u離c1一跳或者c2兩跳時,v=0 //當v=0時,u的子節點不能全為0,u可以放置東西,如果一個點上內有多個v值,取最高的 // //dp[u][0]為在u不部署,而且父節點不為0的情況 // dp[u][1]為在u部署C1,dp[u][2]為在u部署C2 // dp[u][3]為在u不部署,而且父節點為0的情況,也就是說,子節點有一個必須為2 // 覆蓋以u為節點的子樹的最小代價//dp[u][0]為覆蓋子樹,且向上覆蓋長度為0,就是u不放,u的子節點只能放c1 // dp[u][1]為覆蓋子樹,且向上覆蓋長度為1,就是在u放置c1,或者在u的子節點放c2 // dp[u][2]為覆蓋子樹,且向上覆蓋長度為2,也就是在u放置c2 // dp[u][3]為u的兒子節點為根的子樹被全部覆蓋,但是u和它的兒子之間的邊可能沒被完全覆蓋(就是說父節點上有c2,所以不需要) // [3]是為了處理 1-2-3-4-5-6-7-8-9,c1=10,c2=15這種極端的,只要c2覆蓋3和7,就能覆蓋所有的邊 // 覆蓋以u為節點的子樹的最小代價 vector<int> graph[maxn];void dfs(int u,int fa){dp[u][0]=0;dp[u][1]=c1;dp[u][2]=c2;dp[u][3]=0;if(graph[u].empty()) return;int optimal=INF,total=0;for(auto iter=graph[u].begin();iter!=graph[u].end();++iter){ //遍歷子節點int v=*iter;if(v==fa) continue;dfs(v,u);int t=min(dp[v][0],min(dp[v][1],dp[v][2]));dp[u][0]+=dp[v][1]; //u的子節點v是1,u才能是0(向上覆蓋長度0),不能有2,否則就會向上覆蓋dp[u][1]+=t; //向上覆蓋長度是1,有兩種選擇: 1.在u上放置c1,v上1,2,3隨意 2.u上不放,在一個v上放置c2,然后其他的v上隨意,dp[u][2]+=min(t,dp[v][3]); //u放置了c2,子節點v處于什么狀態(0,1,2,3)都行dp[u][3]+=t; //因為是狀態3,所以u和v之間的邊被它的父節點覆蓋,所以v不受上面影響,可以當作一個單獨的樹看待optimal=min(optimal,dp[v][2]-t); //其中一個v上放置c2,選擇增加費用最小的一個v}total=optimal+dp[u][3];dp[u][1]=min(dp[u][1],total); }int main(void){while(cin>>n>>c1>>c2 && n){ // memset(dp,INF,sizeof(dp));for(int i=1;i<=n;++i) graph[i].clear();for(int i=1;i<n;++i){scanf("%d%d",&a,&b);graph[a].push_back(b);graph[b].push_back(a);}dfs(1,-1);printf("%d\n",min(dp[1][0],min(dp[1][0],dp[1][1])));} }C 數列
題意
題解
題目中說最后的時候a=0,b=0,c=0作為結束標志
最后一行的元素一定是上一行的答案
那我們就可以逆著往前推導
根據公式
lastans就是上一行的答案,把下一行的三個式子帶入第一行,就可以求出公式,可以解出再上個提問的答案。。。
代碼
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=50004; const int man=5e5+3; ll w[maxn]; struct node{ll a,b,c; }x[man]; ll a1; ll sum[man]; int main() { // freopen("in.txt","r",stdin);int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%lld",&w[i]);}int tot=1;while(~scanf("%lld%lld%lld",&x[tot].a,&x[tot].b,&x[tot].c)){tot++; // if(x[tot-1].a==-3&&x[tot-1].b==-3&&x[tot-1].c==-3)break; }tot--;sum[tot]=-x[tot].a;for(int i = tot-1; i;i--) {a1 = (0ll + w[sum[i+1]] * sum[i+1] + w[sum[i+1]] *w[sum[i+1]] * (sum[i+1] + 1) + 1);if(a1 == 0) sum[i] = 1;else sum[i] = -(0ll + x[i].a * (sum[i+1] + 1) * w[sum[i+1]] * w[sum[i+1]] + w[sum[i+1]] * sum[i+1] * (x[i].b + 1) + x[i].c + sum[i+1]) / a1;}for(int i=2;i<=tot;i++){printf("%lld\n",sum[i]);}return 0;}D 點名
題目
在體育課上,同學們常常會遲到幾分鐘,但體育老師的點名卻一直很準時。
老師只關心同學的身高,他會依次詢問當前最矮的身高,次矮的身高,第三矮的身
高,等等。在詢問的過程中,會不時地有人插進隊伍里。你需要回答老師每次的詢問
題解
使用大根堆和小根堆來解決第k小的問題
建立一個大根堆qu2(大元素在上面),建立一個小根堆qu1
然后大根堆qu2來維護第k小
每次有學生入隊(新增元素進入我們的兩個堆)
我們先讓學生進入大根堆qu2,之后讓qu2中最大的元素進入小
根堆qu1,這一步目的是為了時刻保持我們大根堆qu2中最大值是我們的第k大,也時刻保持qu2中元素只有k個。
如果我們想求第k+1大,只需將qu1中的最小值調入我們qu2中
。我們qu2中的最大值就是我們的第k+1大。
代碼
#include<iostream> #include<algorithm> #include<cstdio> #include<queue> typedef long long ll; using namespace std; const int maxn=3e4+9; long long a[maxn]; long long b[maxn]; priority_queue <ll,vector<ll>,greater<ll> >q1; priority_queue <ll,vector<ll>,less<ll> >q2; int main() {int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%lld",&a[i]);}for(int i=1;i<=m;i++){scanf("%lld",&b[i]);}for(int i=1;i<=m;i++){for(int j=b[i-1]+1;j<=b[i];j++){q2.push(a[j]);q1.push(q2.top());q2.pop();}q2.push(q1.top());q1.pop();printf("%lld\n",q2.top());}return 0; }E 螞蟻
F 耐久度
G 內存管理
H 采集香料
I 青蛙過河
J 膜法區間
總結
以上是生活随笔為你收集整理的2020年首届算法竞赛网络挑战赛直播讲解课程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 淘宝首页html第一行怎么做的(淘宝首页
- 下一篇: 牛客网 【每日一题】7月27日题目精讲—