2020.07.17模拟3
虎哥出题总是奇奇妙妙,难以琢磨。。。。。。
直接来水今天的题解
A. 中中救援队
不想粘贴题目了,就是安慰奶牛原题原码,kruskal的板子,主要就是建边的时候把点权加进边权就好了,打好板子。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
inline int read(){
int x = 0, w = 1;
char ch = getchar();
for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
return x * w;
}
const int maxn = 1000100;
int fa[maxn], a[maxn];
int n, m;
struct node{
int u, v, w;
inline bool operator < (const node &x) const {
return x.w > w;
}
}edge[maxn << 1];
int head[maxn], tot;
inline int find(int x){
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
int s, t;
inline int kruskal(){
int ans = 0;
int k = 0;
sort(edge + 1, edge + 1 + m);
for(int i = 1; i <= m * 2; i++){
int x = edge[i].u;
int y = edge[i].v;
int xx = find(x);
int yy = find(y);
if(xx == yy) continue;
if(xx != yy){
fa[xx] = fa[yy];
ans += edge[i].w;
k++;
}
if(k == n - 1) break;
}
return ans;
}
int minn = 0x7fffffff;
signed main(){
n = read(); m = read();
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = 1; i <= n; i++){
a[i] = read();
minn = min(minn, a[i]);
}
for(int i = 1; i <= m; i++){
int u = read(), v = read(), w = read();
edge[i].u = u;
edge[i].v = v;
edge[i].w = w * 2 + a[u] + a[v];
edge[i + m].u = v;
edge[i + m].v = u;
edge[i + m].w = w * 2 + a[u] + a[v];
}
int tmp = kruskal();
int ans = tmp + minn;
cout << ans << endl;
return 0;
}
B. 家务活
\(O(n^2)\)暴力切掉,youxam拓扑排序卡过
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
//#define int long long
using namespace std;
inline int read(){
int x = 0, w = 1;
char ch = getchar();
for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
return x * w;
}
const int maxn = 20010;
int t[maxn];
int ans;
signed main(){
// freopen("data.in", "r", stdin);
int n = read();
for(int i = 1; i <= n; i++){
ans = 0;
t[i] = read();
int k = read();
for(int p = 1; p <= k; p++){
int u = read();
ans = max(ans, t[u]);
}
t[i] = ans + t[i];
}
ans = 0;
for(int i = 1; i <= n; i++)
ans = max(ans, t[i]);
cout << ans << endl;
return 0;
}
C. 传纸条
方格取数原题原码(数据范围变大)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
//#define int long long
using namespace std;
inline int read(){
int x = 0, w = 1;
char ch = getchar();
for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
return x * w;
}
const int maxn = 51;
int que[maxn][maxn];
int dp[maxn][maxn][maxn][maxn];
int main(){
int n = read(), m = read();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
que[i][j] = read();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
for(int l = 1; l <= n; l++)
for(int r = 1; r <= m; r++){
if(j == r && i == l)
dp[i][j][l][r] = que[i][j] + max(max(dp[i - 1][j][l - 1][r], dp[i - 1][j][l][r - 1]), max(dp[i][j - 1][l - 1][r], dp[i][j - 1][l][r - 1]));
else
dp[i][j][l][r] = que[i][j] + que[l][r] + max(max(dp[i - 1][j][l - 1][r], dp[i - 1][j][l][r - 1]), max(dp[i][j - 1][l - 1][r], dp[i][j - 1][l][r - 1]));
}
cout << dp[n][m][n][m] << endl;
return 0;
}
D. 杀人游戏
题目描述
一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面查出谁是杀手。
警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。假如查证的对象是杀手,杀手将会把警察干掉。
现在警察掌握了每一个人认识谁。
每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?
输入格式
第一行有两个整数 N,M。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x,例如hjt同志)
输出格式
仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。
样例输入
7 6
4 1
5 4
2 1
7 3
1 6
2 5
样例输出
0.714286
solution
这个以概率为背景属实有点晦涩,不过着眼看着像一个tarjan,考场yy再次上演
这次还yy对了一部分,明明是按照有向图存的图,但是不知道为什么我的tarjan跑不出来
就冒险改成了无向图
然后就玄学的把样例都过掉了
可能是样例具有共同的性质找的规律一并搞掉了
正解
一个联通分量中询问一个点足矣
所以就缩点
然后找入度为0的点
假如对于全图有size为1的点,可以通过询问其他点的结果来判定其是不是杀手
所以可以不加判断,在最后的cnt上-1
代码就是板子,没什么难的
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define int long long
using namespace std;
template<typename T>inline void read(T &a){
char c=getchar();T x=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
a=f*x;
}
const int maxn = 500005;
int n,m,h[maxn],vis[maxn],low[maxn],dfn[maxn],num,tot,sta[maxn],top,cnt,col[maxn],siz[maxn];
int u[maxn],v[maxn],in[maxn],flag,ans;
struct node{
int nex,to;
}edge[maxn];
inline void add(int u,int v){
edge[++tot].nex=h[u];
edge[tot].to=v;
h[u]=tot;
}
inline void tarjan(int x){
dfn[x]=low[x]=++num;
sta[++top]=x;vis[x]=1;
for(int i=h[x];i;i=edge[i].nex){
int xx=edge[i].to;
if(!dfn[xx]){
tarjan(xx);
low[x]=min(low[x],low[xx]);
}
else if(vis[xx])low[x]=min(low[x],dfn[xx]);
}
if(low[x]==dfn[x]){
int now=-1;cnt++;
while(now!=x){
now=sta[top];
top--;
vis[now]=0;
siz[cnt]++;
col[now]=cnt;
}
}
}
signed main(){
read(n);read(m);
for(int i=1;i<=m;i++)
read(u[i]),read(v[i]),add(u[i],v[i]);
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
memset(h,0,sizeof(h));tot=0;
for(int i=1;i<=m;i++)
if(col[u[i]]!=col[v[i]]){
in[col[v[i]]]++;
add(col[u[i]],col[v[i]]);//缩点后重新建图
}
for(int i=1;i<=cnt;i++){
if(!flag&&!in[i]&&siz[i]==1){//找上述符合条件的点
int pd=0;
for(int j=h[i];j;j=edge[j].nex){
int xx=edge[j].to;
if(in[xx]==1)pd=1;
}
if(!pd)flag=1;
}
if(!in[i])ans++;
}
if(flag)ans--;//若标记则可以少一次询问
printf("%.6f\n",1.0-(double)ans/(double)n);
return 0;
}
风吹过,我来过~