Loading

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;
}
posted @ 2020-07-17 14:37  Gary_818  阅读(55)  评论(0编辑  收藏  举报