2020年首届算法竞赛网络挑战赛直播讲解课程
比賽鏈接
菜雞的我,第四名。。
A 矛盾激化
題意
給定地圖,這個(gè)地圖有兩個(gè)出口,現(xiàn)在我們需要求出從所有點(diǎn)到任意一個(gè)出口的距離中的最短路徑的最大值
本題為輸出答案題,給定你一種情況,然后輸出它的答案
題解
如果實(shí)在不會(huì),可以手查(滑稽)
如果直接從每個(gè)點(diǎn)開(kāi)始搜一遍最短距離不現(xiàn)實(shí)
因?yàn)橹挥袃蓚€(gè)出口,所以我們可以從出口出發(fā)然后跑兩邊BFS,每個(gè)點(diǎn)都維護(hù)一個(gè)距離的最小值
有幾個(gè)坑點(diǎn):
1.從出口往里面跳的時(shí)候只能跳一個(gè)格子
2.從別的格子跳往其他格子時(shí)只能跳兩個(gè)格子
3.跳兩格的時(shí)候還要判斷中間隔過(guò)去的一格是不是空格
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) {//當(dāng)從出口往里跳 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);//從兩個(gè)出口開(kāi)始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 我去前面探探路
題意
對(duì)于一個(gè)樹(shù),我們可以進(jìn)行兩種操作,第一種:選擇一個(gè)節(jié)點(diǎn),與該節(jié)點(diǎn)相連的邊會(huì)被標(biāo)記。第二種:選擇第一個(gè)節(jié)點(diǎn),與該節(jié)點(diǎn)相連的邊會(huì)被標(biāo)記,同時(shí)與該節(jié)點(diǎn)相連的節(jié)點(diǎn)的相連的邊也會(huì)被標(biāo)記。這兩種操作有各自的花費(fèi)。問(wèn)將所有邊標(biāo)記所需花費(fèi)是多少
題解
樹(shù)形dp
我們可以發(fā)現(xiàn),在兒子節(jié)點(diǎn)操作2,與自己操作1對(duì)父親節(jié)點(diǎn)的影響一樣的
也就是這兩種情況在后效性上是等價(jià)的
也就是他自己其實(shí)也可以被對(duì)兄弟操作2給影響
我們可以得到:
dp[u][0] :以點(diǎn)u為根的子樹(shù)下的邊全部被覆蓋,且沒(méi)有向u節(jié)點(diǎn)上方覆蓋。也就是u不放,u的子節(jié)點(diǎn)只能進(jìn)行操作1
dp[u][1]:以點(diǎn)u為根的子樹(shù)下的邊全部覆蓋,且向上覆蓋長(zhǎng)度為1。u進(jìn)行操作1,或者u為空,u的子節(jié)點(diǎn)進(jìn)行操作2
dp[u][2]:以點(diǎn)u為根的子樹(shù)下的邊全部覆蓋,且向上覆蓋長(zhǎng)度為2。u進(jìn)行操作2
dp[u][3]:以點(diǎn)u為根的子樹(shù)的子樹(shù)里的邊都被覆蓋,但是u和子樹(shù)間的邊不一定被覆蓋。也就是父親節(jié)點(diǎn)進(jìn)行操作2
v是u的兒子節(jié)點(diǎn)
dp[u][3]是為了處理 1-2-3-4-5-6-7-8-9,c1=10,c2=15這種極端的情況,只要c2覆蓋3和7,就能覆蓋所有的邊
dp[u][j]是不同情況 覆蓋以u(píng)為節(jié)點(diǎn)的子樹(shù)的最小代價(jià)
int t=min(dp[v][0],min(dp[v][1],dp[v][2]));dp[u][0]+=dp[v][1]; //u的子節(jié)點(diǎn)v是1,u才能是0(向上覆蓋長(zhǎng)度0),不能有2,否則就會(huì)向上覆蓋dp[u][1]+=t; //向上覆蓋長(zhǎng)度是1,有兩種選擇: 1.在u上放置c1,v上1,2,3隨意 2.u上不放,在一個(gè)v上放置c2,然后其他的v上隨意,dp[u][2]+=min(t,dp[v][3]); //u放置了c2,子節(jié)點(diǎn)v處于什么狀態(tài)(0,1,2,3)都行dp[u][3]+=t; //因?yàn)槭菭顟B(tài)3,所以u(píng)和v之間的邊被它的父節(jié)點(diǎn)覆蓋,所以v不受上面影響,可以當(dāng)作一個(gè)單獨(dú)的樹(shù)看待optimal=min(optimal,dp[v][2]-t); //其中一個(gè)v上放置c2,選擇增加費(fèi)用最小的一個(gè)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,把放置的裝置看作勢(shì),也就是v的值,然后勢(shì)向周?chē)饘訙p小 // 當(dāng)u上放置c2,v=2,u上放置c1或者離c2一跳是v=1,當(dāng)u離c1一跳或者c2兩跳時(shí),v=0 //當(dāng)v=0時(shí),u的子節(jié)點(diǎn)不能全為0,u可以放置東西,如果一個(gè)點(diǎn)上內(nèi)有多個(gè)v值,取最高的 // //dp[u][0]為在u不部署,而且父節(jié)點(diǎn)不為0的情況 // dp[u][1]為在u部署C1,dp[u][2]為在u部署C2 // dp[u][3]為在u不部署,而且父節(jié)點(diǎn)為0的情況,也就是說(shuō),子節(jié)點(diǎn)有一個(gè)必須為2 // 覆蓋以u(píng)為節(jié)點(diǎn)的子樹(shù)的最小代價(jià)//dp[u][0]為覆蓋子樹(shù),且向上覆蓋長(zhǎng)度為0,就是u不放,u的子節(jié)點(diǎn)只能放c1 // dp[u][1]為覆蓋子樹(shù),且向上覆蓋長(zhǎng)度為1,就是在u放置c1,或者在u的子節(jié)點(diǎn)放c2 // dp[u][2]為覆蓋子樹(shù),且向上覆蓋長(zhǎng)度為2,也就是在u放置c2 // dp[u][3]為u的兒子節(jié)點(diǎn)為根的子樹(shù)被全部覆蓋,但是u和它的兒子之間的邊可能沒(méi)被完全覆蓋(就是說(shuō)父節(jié)點(diǎn)上有c2,所以不需要) // [3]是為了處理 1-2-3-4-5-6-7-8-9,c1=10,c2=15這種極端的,只要c2覆蓋3和7,就能覆蓋所有的邊 // 覆蓋以u(píng)為節(jié)點(diǎn)的子樹(shù)的最小代價(jià) 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){ //遍歷子節(jié)點(diǎn)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的子節(jié)點(diǎn)v是1,u才能是0(向上覆蓋長(zhǎng)度0),不能有2,否則就會(huì)向上覆蓋dp[u][1]+=t; //向上覆蓋長(zhǎng)度是1,有兩種選擇: 1.在u上放置c1,v上1,2,3隨意 2.u上不放,在一個(gè)v上放置c2,然后其他的v上隨意,dp[u][2]+=min(t,dp[v][3]); //u放置了c2,子節(jié)點(diǎn)v處于什么狀態(tài)(0,1,2,3)都行dp[u][3]+=t; //因?yàn)槭菭顟B(tài)3,所以u(píng)和v之間的邊被它的父節(jié)點(diǎn)覆蓋,所以v不受上面影響,可以當(dāng)作一個(gè)單獨(dú)的樹(shù)看待optimal=min(optimal,dp[v][2]-t); //其中一個(gè)v上放置c2,選擇增加費(fèi)用最小的一個(gè)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 數(shù)列
題意
題解
題目中說(shuō)最后的時(shí)候a=0,b=0,c=0作為結(jié)束標(biāo)志
最后一行的元素一定是上一行的答案
那我們就可以逆著往前推導(dǎo)
根據(jù)公式
lastans就是上一行的答案,把下一行的三個(gè)式子帶入第一行,就可以求出公式,可以解出再上個(gè)提問(wèn)的答案。。。
代碼
#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 點(diǎn)名
題目
在體育課上,同學(xué)們常常會(huì)遲到幾分鐘,但體育老師的點(diǎn)名卻一直很準(zhǔn)時(shí)。
老師只關(guān)心同學(xué)的身高,他會(huì)依次詢(xún)問(wèn)當(dāng)前最矮的身高,次矮的身高,第三矮的身
高,等等。在詢(xún)問(wèn)的過(guò)程中,會(huì)不時(shí)地有人插進(jìn)隊(duì)伍里。你需要回答老師每次的詢(xún)問(wèn)
題解
使用大根堆和小根堆來(lái)解決第k小的問(wèn)題
建立一個(gè)大根堆qu2(大元素在上面),建立一個(gè)小根堆qu1
然后大根堆qu2來(lái)維護(hù)第k小
每次有學(xué)生入隊(duì)(新增元素進(jìn)入我們的兩個(gè)堆)
我們先讓學(xué)生進(jìn)入大根堆qu2,之后讓qu2中最大的元素進(jìn)入小
根堆qu1,這一步目的是為了時(shí)刻保持我們大根堆qu2中最大值是我們的第k大,也時(shí)刻保持qu2中元素只有k個(gè)。
如果我們想求第k+1大,只需將qu1中的最小值調(diào)入我們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 內(nèi)存管理
H 采集香料
I 青蛙過(guò)河
J 膜法區(qū)間
總結(jié)
以上是生活随笔為你收集整理的2020年首届算法竞赛网络挑战赛直播讲解课程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 淘宝首页html第一行怎么做的(淘宝首页
- 下一篇: 牛客网 【每日一题】7月27日题目精讲—