Codeforces Round #408 (Div. 2) 思维,模拟,DP, 多点BFS
2017-05-02 16:31
399 查看
题目链接:http://codeforces.com/contest/796
A:水题,看懂题意然后模拟一下即可。复杂度O(n)
B,同模拟水题,复杂度O(k)
C:题意就是给了一颗树,每个节点有一个能量值,现在一个人有个初始能量值,现在他选择一个点去破坏,
但是破坏之后这个点的相邻点和相邻点的相邻点的能量值会加1,破坏一个点的条件是这个人当前的能量值
不小于这个点的能量值。问破坏图中所有节点具有的初始能量的最小值。
解法:思维,思考一下发现如果我们选择最大的好像很能逼近正确答案,那么一定是最大吗?并不是我们可以
简单举出一些例子发现答案可以是最大值加1或者最大值加2,但是好像没有其他答案了?的确,我们深入思
考,这是一颗树,考虑极限情况就是1条链并且所有值都等于最大值时,我们用最大值加2就可以了。那么
什么情况是最大值,什么情况是最大值加一,什么情况是最大值加二呢?
最大值:只有一个点的值等于mx,相邻点包含了全部mx-1的点
最大值加一:存在一个点,它和它相邻的点,包含了全部的权值为最大值的点。
最大值加二 : 其他。
并且只有一个点为mx并且不满足条件1的时候,答案是mx+1。
D:
题意:n个城市,某些城市有警察局,共k个。定义了一个规则,就是任意一个点离它最近的警察局的距离都要
不超过d。边权是1,现在问在满足这个规则的条件下最多删除多少条边?
解法:开始想的是,删除的边肯定和k个点能延伸d步能覆盖到的边的覆盖,然后就不知道怎么做了。然后观摩
了一发我队友的代码,偷取了一个思路。。。把k个起点放到队列里面,进行多起点BFS。维护两个标记,一
个标记边,一个标记点,如果当前边没有被标记但是当前点的下一个点被标记了,则说明了当前边是可以删除
的,理由很简单,因为BFS是按照距离从小到大跑的,如果当前边没有被标记但是当前点的下一个点被标记
了,也就说明了dis[u]<=d&&dis[v]<=d。并且我们这道题根本不需要关心d,因为题目说了初始状态是合法的
那么BFS跑最短路怎么都不会跑到大于d的距离。
E
题意:有一个人要考试作弊, 她可以偷看左右两个人的试卷。但是她只能偷看p次,每次只能看一个人试卷上
的连续的k个题目。总共有n道题目,给你左右两个人能做对的题目,求出她最大能做对几道题。
一个样例:她共有2次偷看的机会,每次只能看3道题目,第一次偷看person1的(1,2,3), 第二次偷看
person2的(4, 5, 6),可以作对的题目为(1, 3, 5, 6)共4道
解法:DP
dp[i][j][x][y]表示考虑完前i个问题,当前用掉了j次机会,第一个人还能看x道题,第二个人还能看j道题的最优值。
这里的复杂度O(p*k*k),但是当p>(2*n)/k时可以全选,那么直接全选就行了。那么剩下的我们就跑个DP就
好了。复杂度是O(n*n*k)
那么dp的转移这里用被动转移好写一点,并且注意到数组直接是开不下的,观察转移发现可以滚动一下,这
题就完了。转移看代码,不难。
A:水题,看懂题意然后模拟一下即可。复杂度O(n)
///CF 796A #include <bits/stdc++.h> using namespace std; int n, m, k; int a[110]; int main(){ scanf("%d%d%d", &n,&m,&k); for(int i=1; i<=n; i++){ scanf("%d", &a[i]); } int ans = 1e9; for(int i=m-1; i>=1; i--){ if(a[i]!=0&&a[i]<=k){ ans = min(ans, (m-i)*10); } } for(int i=m+1; i<=n; i++){ if(a[i]!=0&&a[i]<=k){ ans = min(ans, (i-m)*10); } } cout<<ans<<endl; }
B,同模拟水题,复杂度O(k)
///CF 796B #include <bits/stdc++.h> using namespace std; const int maxn = 1e6+10; int n, m, k; int vis[maxn]; int main(){ scanf("%d%d%d", &n,&m,&k); for(int i=1; i<=m; i++){ int x; scanf("%d", &x); vis[x]=1; } int curpos = 1; int ans = -1; int flag = 0; while(k--){ int x, y; scanf("%d%d", &x, &y); if(flag) continue; if(x==curpos&&vis[x]){ flag=1; ans=x; } else if(y==curpos&&vis[y]){ flag=1; ans=y; } else if(x==curpos){ if(vis[y]){ ans = y; flag=1; } else{ curpos=y; } } else if(y==curpos){ if(vis[x]){ ans = x; flag = 1; } else{ curpos = x; } } } if(ans==-1) ans = curpos; cout<<ans<<endl; return 0; }
C:题意就是给了一颗树,每个节点有一个能量值,现在一个人有个初始能量值,现在他选择一个点去破坏,
但是破坏之后这个点的相邻点和相邻点的相邻点的能量值会加1,破坏一个点的条件是这个人当前的能量值
不小于这个点的能量值。问破坏图中所有节点具有的初始能量的最小值。
解法:思维,思考一下发现如果我们选择最大的好像很能逼近正确答案,那么一定是最大吗?并不是我们可以
简单举出一些例子发现答案可以是最大值加1或者最大值加2,但是好像没有其他答案了?的确,我们深入思
考,这是一颗树,考虑极限情况就是1条链并且所有值都等于最大值时,我们用最大值加2就可以了。那么
什么情况是最大值,什么情况是最大值加一,什么情况是最大值加二呢?
最大值:只有一个点的值等于mx,相邻点包含了全部mx-1的点
最大值加一:存在一个点,它和它相邻的点,包含了全部的权值为最大值的点。
最大值加二 : 其他。
并且只有一个点为mx并且不满足条件1的时候,答案是mx+1。
///CF 469C #include <bits/stdc++.h> using namespace std; const int maxn = 3e5+10; map <int, int> mp1; int n, a[maxn]; vector<int>G[maxn]; int main() { scanf("%d", &n); for(int i=1; i<=n; i++) scanf("%d", &a[i]); for(int i=1; i<n; i++) { int x, y; scanf("%d%d", &x,&y); G[x].push_back(y); G[y].push_back(x); } int mx = -2e9; for(int i=1; i<=n; i++) mx = max(a[i], mx); for(int i=1; i<=n; i++) { mp1[a[i]]++; } int ans = mx+2; if(mp1[mx]==1) { int pos; for(int i=1; i<=n; i++) if(a[i]==mx) { pos=i; break; } int cnt = 0; for(int i=0; i<G[pos].size(); i++) { int v = G[pos][i]; if(a[v]==mx-1) cnt++; } if(cnt==mp1[mx-1]) ans = mx; else ans=mx+1; printf("%d\n", ans); return 0; } else for(int i=1; i<=n; i++){ int cnt=0; if(a[i]==mx) cnt++; for(int j=0; j<G[i].size(); j++) { int v = G[i][j]; if(a[v]==mx) cnt++; } if(cnt==mp1[mx]) { ans = mx+1; printf("%d\n", ans); return 0; } } printf("%d\n", ans); }
D:
题意:n个城市,某些城市有警察局,共k个。定义了一个规则,就是任意一个点离它最近的警察局的距离都要
不超过d。边权是1,现在问在满足这个规则的条件下最多删除多少条边?
解法:开始想的是,删除的边肯定和k个点能延伸d步能覆盖到的边的覆盖,然后就不知道怎么做了。然后观摩
了一发我队友的代码,偷取了一个思路。。。把k个起点放到队列里面,进行多起点BFS。维护两个标记,一
个标记边,一个标记点,如果当前边没有被标记但是当前点的下一个点被标记了,则说明了当前边是可以删除
的,理由很简单,因为BFS是按照距离从小到大跑的,如果当前边没有被标记但是当前点的下一个点被标记
了,也就说明了dis[u]<=d&&dis[v]<=d。并且我们这道题根本不需要关心d,因为题目说了初始状态是合法的
那么BFS跑最短路怎么都不会跑到大于d的距离。
///CF 796D #include <bits/stdc++.h> using namespace std; const int maxn = 3e5+10; ///u&&v连通 dis[u]<=d&&dis[v]<=d ///本质上变成了本题实际和d无关,因为初始就是合法状态 struct node{ int v, id; }; int n, m, d; vector <node> G[maxn]; int vis1[maxn], vis2[maxn]; queue <int> que; int bfs() { int ans = 0; while(!que.empty()) { int u=que.front(); que.pop(); for(int i=0; i<G[u].size(); i++){ node nxt = G[u][i]; if(vis1[nxt.id]) continue; if(vis2[nxt.v]){ vis1[nxt.id]=2; ans++; continue; } vis2[nxt.v]=1; vis1[nxt.id]=1; que.push(nxt.v); } } printf("%d\n", ans); } int main() { scanf("%d%d%d", &n,&m,&d); for(int i=0; i<m; i++){ int x; scanf("%d", &x); vis2[x]=1; que.push(x); } for(int i=1; i<n; i++){ int u, v; scanf("%d%d", &u,&v); G[u].push_back(node{v, i}); G[v].push_back(node{u, i}); } int ans = bfs(); //cout<<ans<<endl; for(int i=1; i<n; i++){ if(vis1[i]==2) printf("%d ", i); } cout<<endl; }
E
题意:有一个人要考试作弊, 她可以偷看左右两个人的试卷。但是她只能偷看p次,每次只能看一个人试卷上
的连续的k个题目。总共有n道题目,给你左右两个人能做对的题目,求出她最大能做对几道题。
一个样例:她共有2次偷看的机会,每次只能看3道题目,第一次偷看person1的(1,2,3), 第二次偷看
person2的(4, 5, 6),可以作对的题目为(1, 3, 5, 6)共4道
解法:DP
dp[i][j][x][y]表示考虑完前i个问题,当前用掉了j次机会,第一个人还能看x道题,第二个人还能看j道题的最优值。
这里的复杂度O(p*k*k),但是当p>(2*n)/k时可以全选,那么直接全选就行了。那么剩下的我们就跑个DP就
好了。复杂度是O(n*n*k)
那么dp的转移这里用被动转移好写一点,并且注意到数组直接是开不下的,观察转移发现可以滚动一下,这
题就完了。转移看代码,不难。
///CF 769E #include <bits/stdc++.h> using namespace std; const int maxn = 1002; int n, p, k, a[maxn], b[maxn], ans; int n1, n2, dp[2][maxn][52][52]; ///dp[i][j][x][y]表示考虑完前i个问题,当前用掉了j次机会,第一个人还能看x道题,第二个人还能看j道题的最优值 void update(int &x, int y) {if(y>x) x=y;} int now=0,pre=1; int main() { scanf("%d%d%d", &n,&p,&k); scanf("%d",&n1); for(int i=1; i<=n1; i++){ int x; scanf("%d", &x); a[x]++; } scanf("%d", &n2); for(int i=1; i<=n2; i++){ int x; scanf("%d", &x); b[x]++; } int lim=n/p; if(n%p!=0) lim++; lim*=2; if(k>=lim){ for(int i=1; i<=n; i++){ ans += a[i]|b[i]; } printf("%d\n", ans); return 0; } memset(dp, 0xef, sizeof(dp)); dp[0][0][0][0]=0; for(int i=1; i<=n; i++){ swap(now,pre); memset(dp[now], 0xef, sizeof(dp[now])); for(int j=0; j<=p; j++){ for(int ii=0; ii<=k; ii++){ for(int jj=0; jj<=k; jj++){ if(!ii&&!jj){ update(dp[now][j][0][0], dp[pre][j][0][0]); update(dp[now][j+1][k-1][0], dp[pre][j][0][0]+a[i]); update(dp[now][j+1][0][k-1], dp[pre][j][0][0]+b[i]); update(dp[now][j+2][k-1][k-1], dp[pre][j][0][0]+(a[i]|b[i])); } else if(!ii){ update(dp[now][j][0][jj-1], dp[pre][j][0][jj]+b[i]); update(dp[now][j+1][k-1][jj-1], dp[pre][j][0][jj]+a[i]); update(dp[now][j+2][k-1][k-1], dp[pre][j][0][jj]+(a[i]|b[i])); } else if(!jj){ update(dp[now][j][ii-1][0], dp[pre][j][ii][0]+a[i]); update(dp[now][j+1][ii-1][k-1], dp[pre][j][ii][0]+b[i]); update(dp[now][j+2][k-1][k-1], dp[pre][j][ii][0]+(a[i]|b[i])); } else update(dp[now][j][ii-1][jj-1], dp[pre][j][ii][jj]+(a[i]|b[i])); } } } } for(int l=0; l<=p; l++){ for(int i=0; i<=k; i++){ for(int j=0; j<=k; j++){ ans = max(ans, dp[now][l][i][j]); } } } printf("%d\n", ans); return 0; }
相关文章推荐
- Codeforces Round #301 (Div. 2)A B C D 水 模拟 bfs 概率dp
- [NOIP模拟][审题][数据结构][bfs/dp]
- Codeforces Round #210 (Div. 1) B. Levko and Array(dp 思维)
- [NOIP模拟][bfs][dp][CSD][表达式的计算]
- 10.10 高校模拟赛 贪心模拟+BFS+DP
- 【Codeforces Round 336 (Div 2) C】【贪心 DP思维】Chain Reaction 每个灯塔位置为a[]破坏b[]范围所有灯塔 设置一个灯塔使得最多灯塔被保留
- Codeforces Round #277.5(Div. 2) F. Special Matrices【思维+Dp】好题~好题~
- Codeforces Round #180 (Div. 2) A. Snow Footprints 【思维+模拟】
- Codeforces Round #336 (Div. 2)【A.思维,暴力,B.字符串,暴搜,前缀和,C.暴力,D,区间dp,E,字符串,数学】
- Codeforces Round #423 (Div. 2) C. String Reconstruction(思维 模拟)
- srm 302 div2 1000(简单题,bfs,dp)
- Codeforces Round #277 (Div. 2) D. Valid Sets (DP DFS 思维)
- Codeforces #324 Div2 B.Kolya and Tanya(dp、思维)
- Codeforces Round #386 (Div. 2)C. Tram(模拟+思维)
- Codeforces Round #354 (Div. 2) B. Pyramid of Glasses (模拟+思维)
- WUST 2090 HLD与停车场(bfs||dfs||暴力模拟||巧妙思维)
- Codeforces Round #363 (Div. 2) C. Vacations【n-dp(max) or 暴力模拟】
- Codeforces Round #434(div2)B-模拟&思维&读题-Which floor?
- Codeforces Round #439 (Div. 2)(补题) A模拟+set B 数学 C dp or 杨辉三角组合数
- Codeforces Round #426 (Div. 2) C. The Meaningless Game 思维 D. The Bakery dp