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;
    }
    
  • 相关阅读:
    Windows Store App 主题动画
    Windows Store App 过渡动画
    Windows Store App 控件动画
    Windows Store App 近期访问列表
    Windows Store App 文件选取器
    Windows Store App 访问应用内部文件
    Windows Store App 用户库文件分组
    Windows Store App 获取文件及文件夹列表
    Windows Store App 用户库文件夹操作
    Windows Store App 用户库文件操作
  • 原文地址:https://www.cnblogs.com/czhui666/p/13812646.html
Copyright © 2011-2022 走看看