A.Abstract Picture Gym - 100917A
分析:由于最后刷的一笔肯定使得某一行或者是某一列均为相同的颜色。
因此我们可以在一开始找到所有的行或者列,他们的颜色全都一样,把这样的行或列加入到队列里面去。
我们处理在队列里面的行或者列:把它取出来,并且把该笔刷成的颜色全都变成’?’(即可变颜色)。然后检查是否有新的行或者列变成颜色均相同了,如果有的话,继续加入队列中进行处理。
队列出队的顺序反过来即为答案。
具体实现细节略。
F - Find the Length Gym - 100917F
分析:因为不存在重复边,那么一个最短简单环,至少需要有三个点组成,而删掉最短简单环上的一条边uvuv那么剩下的一条路径一定是删去uvuv之后的从uu到的最短路。
对于一定包含点ss的最短简单环,我们枚举这里面假装删掉的那条边,那么会出现下面两种情况
一、这条边的一个端点是
这种情况下,设另一端的端点为tt,如果到tt的最短路就不是这条边,那么很好,从到tt的最短路边stst就是一个可行结果,更新包含ss的最短简单环答案;而如果从到tt的最短路就是这条边,那么这种情况我们直接扔掉,不做处理,因为这种情况可以由枚举其他边的时候解决。
二、这条边的两个端点均非ss
这种情况下,从到vv的最短路还要包含,那么只能是从uu到的最短路++从到ss的最短路,这里千万要注意两条最短路不能有重复边。否则从到vv的最短路将不会包含点。
具体做法:
枚举点ss,跑一边Dijkstra,得到一颗最短路径树,然后根据第一种情况更新一次答案。根据第二种情况更新答案的时候注意,判断两条路不包含重复边的方法可以用,当然也可以用其他的方法。
#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];//map[cas][j] = i;way[cas][j] = way[cas][j-val[cas][i]];way[cas][j].set(i); if(dp[cas][j] >= upb[cas^1]){//output(cas,j);puts("NO");for(int i = 1;i <= n;++i){cout<<way[cas][j][i];}return 0;}}}}}puts("YES");return 0;
}