zoukankan      html  css  js  c++  java
  • 1013考试 乱搞 Tarjan找环 概率DP

    1013考试总结

    T1

    ​ 题目大意:

    ​ 给定(n)个字符串,都是小写字母,现在要把它们缩短,要求如下:

    ​ 1.缩短后的长度一定要小于缩短前的长度。

    ​ 2.缩短后都是小写字母。

    ​ 3.相同的字符串缩短完也相同,不同的字符串缩短后一定不同。

    (n <= 1000)

    ​ 乱搞吧。

    ​ 考场上的想法是先把最后一个字符删去,如果与前面有冲突的就(rand)一个(x),代表第(x)位要修改。因为判冲突的复杂度比较玄,rand好了就很快,rand不好就凉了,最后T了一个点。

    ​ 比较正确的思路是:重构所有的字符串只要他们都合法就行。先把所有的字符串按长度排个序。因为(n <= 1000),所以我们最多需要三位的字符串就够了。开三个变量(a, b, c),范围都是从96到122,也就是小写字母的ASCLL码值。每次碰见一个新字符串就让(c ++),如果(c == 123),就类似于进位,让(b ++)(c = 96)(a)也同理。时间复杂度(O(n log n))

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e3 + 5;
    int n, aa, ab, ac;
    map <string, int> mp;
    struct node { string s; int id, len, last, a, b, c; } a[N];
    
    int cmp(node a, node b) { return a.len < b.len; }
    
    int mmp(node a, node b) { return a.id < b.id; }
    
    int main() {
    
        cin >> n; ac = 96;
        for(int i = 1;i <= n; i++) {
            cin >> a[i].s;
            a[i].len = a[i].s.length();
            a[i].id = i;
        }
        sort(a + 1, a + n + 1, cmp);
        for(int i = 1;i <= n; i++) {
            if(mp[a[i].s]) { 
                a[i].last = mp[a[i].s];
                a[i].a = a[a[i].last].a;
                a[i].b = a[a[i].last].b;
                a[i].c = a[a[i].last].c;
                continue;
            }
            mp[a[i].s] = i; ac ++;
            if(ac == 123) {
                if(ab == 0) ab = 96;
                else ab ++;
                ac = 96;
            }
            if(ab == 123) {
                if(aa == 0) aa = 96;
                else aa ++;
                ab = 96;
            }
            a[i].a = aa; a[i].b = ab; a[i].c = ac;
        }
        sort(a + 1, a + n + 1, mmp);
        for(int i = 1;i <= n; i++) {
            if(a[i].a) cout << (char) a[i].a;
            if(a[i].b) cout << (char) a[i].b;
            if(a[i].c) cout << (char) a[i].c;
            cout << "
    ";
        }
    
        fclose(stdin); fclose(stdout);
        return 0;
    }
    

    T2

    ​ 题目大意:

    ​ 给定(n)个点和(n)条有向边,删一条边后使得只有一个点没有出度,其他所有点只有一个出度,输出删的这条边最大的编号。

    ​ 考试的时候其实已经和正解想法差不多了,但还是一些细节没想到。

    ​ 考试时的想法是分两种情况:1.如果有一个点有两个出度,那肯定是删这两条边编号大的那个。2.如果有环,删除这个环上编号最大的那一条边。

    ​ 但只拿了80pts。

    ​ 错误的地方是:1.第一种情况能删的边一定是可以从根节点到达的那条边,根节点是指出度为0的点。2.我用拓扑找的环,但是拓扑只可以判断图中是否有环,却不可以确定某个点是否真的在环上,所以得用(Tarjan)找环。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 1e6 + 5;
    int n, p, t, cnt, tag, ans, tot, top;
    int in_edge[N], head[N], vis[N], low[N], dfn[N], sta[N], in[N], zhi[N], tt[N];
    struct edge { int u, to, id, nxt; } e[N];
    priority_queue <int> q;
    
    void add(int x, int y, int i) {
        e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y; e[cnt].id = i; e[cnt].u = x;
        in_edge[y] ++; zhi[y] = i; 
    }
    
    void dfs(int x) {
        vis[x] = 1;
        for(int i = head[x]; i ; i = e[i].nxt) {
            int y = e[i].to; if(y == t) continue;
            dfs(y);
        }
    }
    
    void Tarjan(int now){
    	low[now] = dfn[now] = ++ tot; sta[++ top] = now; in[now] = 1;
    	for(int i = head[now]; i; i = e[i].nxt){
    		int to = e[i].to;
    		if(!dfn[to]){
    			Tarjan(to); low[now] = min(low[now], low[to]);
    		}else if(in[to]) low[now] = min(low[now], dfn[to]);
    	}
    	if(dfn[now] == low[now]){
    		int x = sta[top], y = now;
    		do{
    			x = sta[top --];
    			q.push(zhi[x]);
    			in[x] = 0;
    		}while(x != y);
    		if(q.size() > 1) ans = max(ans, q.top());
    		while(q.size()) q.pop();
    	}
    }
    
    int main() {
     
        n = read();
        for(int i = 1, x, y;i <= n; i++) x = read(), y = read(), add(x, y, i);
        for(int i = 1;i <= n; i++) {
            if(in_edge[i] > 1) { tag = 1; t = i; }
            if(!in_edge[i]) { p = i; }
        }
        if(tag) { 
            for(int i = 1; i <= n; i ++){
    			if(e[i].to == t) tt[++ tot] = i;
    		}
    		dfs(p);
    		if(vis[e[tt[1]].u] == 0) ans = tt[1];
    		else{
    			if(vis[e[tt[2]].u] == 0) ans = tt[2];
    			else ans = max(tt[1], tt[2]);
    		}
        }
        else {
            for(int i = 1;i <= n; i++) if(!dfn[i]) Tarjan(i); 
        }
        printf("%d", ans);
    
        fclose(stdin); fclose(stdout);
        return 0;
    }
    

    T3

    ​ 题目大意:

    ​ 有两个人(A, B),两个数(n, k)(n)代表每个人有(n)吨茶,(k)表示(A)每次可以倒出(4k, 3k, 2k, k)吨茶,那么(B)倒出的茶与(A)相加为(5k),也就是说(A)倒出(4k)(B)就倒出(k)。问(A)(B)先到完加上同时倒完的概率是多少。(n, k <= 1e9)

    ​ 考场上完全不会。。。

    ​ 首先可以写出(n ^ 2)暴力:

    (f[i][j])表示(A)倒出(i * k)吨,(B)倒出(j * k)吨的概率,那么dp转移方程可以写出:(f[min(n, i + l * k)][min(n, j + (4 - l) * k)] += 1.0 / 4.0 * f[i][j])

    ​ 然后打个表,发现(n >= 300,k = 1)的时候都会输出1.000000(可以把k转化成1的)。诶嘿嘿嘿暴力变正解,直接(n ^ 2)做就好拉。

    #include <bits/stdc++.h>
        
    using namespace std;
        
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
        
    const int N = 2005;
    int n, k;
    double ans, f[N][N];
    
    int main() {
    
        n = read(); k = read();
        if(n / k > 2000) { printf("1.000000"); return 0; }
        int tmp = n / k; if(n % k) tmp ++;
        n = tmp; k = 1; f[0][0] = 1;
        for(int i = 0;i < n; i++) 
            for(int j = 0;j < n; j++) 
                for(int l = 1;l <= 4; l++) 
                    f[min(n, i + l * k)][min(n, j + (4 - l) * k)] += 1.0 / 4.0 * f[i][j];
        for(int j = 0;j < n; j++) ans += f[n][j];
        ans += f[n][n] / 2.0;
        printf("%.6lf", ans);
    
        fclose(stdin); fclose(stdout);
        return 0;
    }
    
  • 相关阅读:
    币值转换
    抓老鼠啊!亏了还是赚了
    打印沙漏
    秋季学习总结
    记忆中最深刻的三位老师
    自我介绍
    docker 安装redis 和 mysql
    centos 安装docker
    celery的简单使用
    django redis配置和简单使用
  • 原文地址:https://www.cnblogs.com/czhui666/p/13812646.html
Copyright © 2011-2022 走看看