生活随笔
收集整理的這篇文章主要介紹了
Codeforces Gym - 100917 部分题解
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
A.Abstract Picture Gym - 100917A
分析:由于最后刷的一筆肯定使得某一行或者是某一列均為相同的顏色。
因此我們可以在一開始找到所有的行或者列,他們的顏色全都一樣,把這樣的行或列加入到隊列里面去。
我們處理在隊列里面的行或者列:把它取出來,并且把該筆刷成的顏色全都變成’?’(即可變顏色)。然后檢查是否有新的行或者列變成顏色均相同了,如果有的話,繼續加入隊列中進行處理。
隊列出隊的順序反過來即為答案。
具體實現細節略。
F - Find the Length Gym - 100917F
分析:因為不存在重復邊,那么一個最短簡單環,至少需要有三個點組成,而刪掉最短簡單環上的一條邊uvuv那么剩下的一條路徑一定是刪去uvuv之后的從uu到vv的最短路。
對于一定包含點ss的最短簡單環,我們枚舉這里面假裝刪掉的那條邊,那么會出現下面兩種情況
一、這條邊的一個端點是ss
這種情況下,設另一端的端點為tt,如果ss到tt的最短路就不是這條邊,那么很好,從ss到tt的最短路++邊stst就是一個可行結果,更新包含ss的最短簡單環答案;而如果從ss到tt的最短路就是這條邊,那么這種情況我們直接扔掉,不做處理,因為這種情況可以由枚舉其他邊的時候解決。
二、這條邊的兩個端點u、vu、v均非ss
這種情況下,從uu到vv的最短路還要包含ss,那么只能是從uu到ss的最短路++從vv到ss的最短路,這里千萬要注意兩條最短路不能有重復邊。否則從uu到vv的最短路將不會包含點ss。
具體做法:
枚舉點ss,跑一邊Dijkstra,得到一顆最短路徑樹,然后根據第一種情況更新一次答案。根據第二種情況更新答案的時候注意,判斷兩條路不包含重復邊的方法可以用lcalca,當然也可以用其他的方法。
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
typedef pair<
int,
int> pii;
const int N =
400;
const int inf =
1e8;
int dist[N];
int G[N][N];
int n;
int fa[N];
int find(
int x){
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void dij(
int s){
for(
int i =
1;i <= n;++i) dist[i] = inf,fa[i] = i;dist[s] =
0;priority_queue<pii> Q;Q.push({
0,s});
while(!Q.empty()){pii p = Q.top();Q.pop();
int u = p.second;
int d = -p.first;
if(d > dist[u])
continue;
for(
int v =
1;v <= n;++v){
if(v == u)
continue;
if(dist[v] > dist[u] + G[u][v]){dist[v] = dist[u] + G[u][v];
if(u != s) fa[v] = u;Q.push({-dist[v],v});}}}
}
#define pr(x) cout<<#x<<":"<<x<<endl
int main(){
cin>>n;
for(
int i =
1;i <= n;++i){
for(
int j =
1;j <= n;++j){
scanf(
" %d",&G[i][j]);
if(G[i][j] == -
1) G[i][j] = inf;}}
for(
int s =
1;s <= n;++s){
int ans = inf;dij(s);
for(
int i =
1;i <= n;++i){
if(fa[i] != i){
if(dist[i] + G[i][s] < ans)ans = dist[i] + G[i][s];}}
for(
int i =
1;i <= n;++i){
if(i == s)
continue;
for(
int j =
1;j <= n;++j){
if(j == s || find(i) == find(j))
continue;
if(dist[i]+dist[j]+G[i][j] < ans)ans = dist[i]+dist[j]+G[i][j];}}ans = ans == inf ? -
1:ans;
printf(
"%d\n",ans);}
return 0;
}
I - Interactive Casino Gym - 100917I
這道題很有意思。
我們把所有的種子每個都用隨機數生成器計算出前幾個值,并把每個值得到的輸贏用01串存起來,實踐發現當計算到第38層的時候,我們就可以把每個種子的01串給區分開了。對于每個種子,01串的長度為38。
我們將這所有的01串放入一個Trie樹里面去。一開始一直用猜1來試,可以根據反饋結果確定這次的位。38輪以后我們就可以找得到隨機數種子了。找到隨機數種子這道題基本就結束了。
J - Judgement Gym - 100917J
我們用背包dp求出在第一種方案下的權值和為s1s1時候所能得到第二種方案下權值和的最大值s2s2,只要出現s1<ps1<p而s2>=qs2>=q的情況,那么就輸出”NO”并且打印方案。反過來再做一遍就好了。
這里注意這道題要使用滾動數組,這樣的話記錄方案就會變得麻煩,為了保存信息,需要使用一個長為100的bitset來存方案。
#include <iostream>
#include <cstdio>
#include <assert.h>
#include <cstring>
#include <bitset>
using namespace std;
const int N =
1e6+
10;
int n,upb[
2],val[
2][N];
int dp[
2][N];
bitset<101> way[
2][N];
int vis[
111];
int main(){
scanf(
"%d",&n);
scanf(
"%d",&upb[
0]);
for(
int i =
1;i <= n;++i)
scanf(
"%d",&val[
0][i]);
scanf(
"%d",&upb[
1]);
for(
int i =
1;i <= n;++i)
scanf(
"%d",&val[
1][i]);
int cc =
0 ;
for(
int cas =
0;cas <
2;++cas){
for(
int i =
1;i <= n;++i){
for(
int j = upb[cas]-
1;j >= val[cas][i];j--){
if(dp[cas][j-val[cas][i]] + val[cas^
1][i] > dp[cas][j]){dp[cas][j] = dp[cas][j-val[cas][i]] + val[cas^
1][i];way[cas][j] = way[cas][j-val[cas][i]];way[cas][j].
set(i);
if(dp[cas][j] >= upb[cas^
1]){
puts(
"NO");
for(
int i =
1;i <= n;++i){
cout<<way[cas][j][i];}
return 0;}}}}}
puts(
"YES");
return 0;
}
總結
以上是生活随笔為你收集整理的Codeforces Gym - 100917 部分题解的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。