summer training 6-16

summer training 6-16

problem A:
给出一个物体的剖面图,判断其重心是在地基(图中房子的最后一行)的左边,中间平衡,右边,三种情况。
solution:
取每个房子的每个非空点(i, j)拆成两部分(i, j), (i, j + 1),然后每部分求和处于非空点个数,即可统计出重心,再与地基的最左最右端点比较,主页最右端点的特殊处理。

AC code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 200 + 5;
const int INF = 0x3f3f3f3f;
char s[maxn][maxn];
int n, m;
int main(){
    scanf("%d%d",&n,&m);
    for(int i = 0; i < n; ++i){
        scanf("%s", s[i]);
    }
    double sumx = 0, leftx, rightx;
    int cnt = 0, last = 0;
    for(int i = 0; i < n; ++i){
        for(int j = 0; j < m; ++j){
            if(s[i][j] != '.'){
                last = i;
                ++cnt;
                sumx += (j * 1.0 + 0.5);
            }
        }
    }
    sumx = sumx / (cnt * 1.0);
    for(int i = 0; i < m; ++i){
        if(s[last][i] != '.'){
            leftx = i * 1.0;
            break ;
        }
    }
    for(int i = m - 1; i >= 0; --i){
        if(s[last][i] != '.'){
            rightx = (i * 1.0) + 1.0;
            break ;
        }
    }
    if(sumx < leftx){
        puts("left");
    } else if(sumx > rightx){
        puts("right");
    } else{
        puts("balanced");
    }
    return 0;
}

Problem B:
统计每个连通块的点个数,贪心选取最大的几个连通块使其点总数大于题目给定值h即可。
排序没有特判h=0的情况,wa到自闭,最后一发优先队列过去了。

AC code:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1014;
int dir1[6][2] = {-1, 0, -1, 1, 0, -1, 0, 1, 1, 0, 1, 1};
int dir2[6][2] = {1, -1, 1, 0, 0, 1, 0, -1, -1, -1, -1, 0};
char s[maxn][maxn];
int h,n,m;
int c = 0;
bool vis[maxn][maxn];
void dfs(int x, int y)
{
    vis[x][y] = 1;
    c++;
    for(int i = 0; i < 6; ++i)
    {
        int nx = x + ((x % 2 == 1) ? dir1[i][0] : dir2[i][0]);
        int ny = y + ((x % 2 == 1) ? dir1[i][1] : dir2[i][1]);
        if(nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
        if(vis[nx][ny] || (s[nx][ny] == '#')) continue;
        vis[nx][ny] = 1;
        dfs(nx,ny);
    }
}

bool cmp(const int a, const int b){
    return a > b;
}

int main()
{
    ios::sync_with_stdio(false);
    priority_queue<int, vector<int>, less<int> > que;
    cin >> h >> n >> m;
    for(int i = 0; i < n; ++i)
    {
        for(int j = 0; j < m; ++j)
            cin >> s[i][j];
    }
    memset(vis, false, sizeof(vis));
    for(int i = 0; i < n; ++i)
    {
        for(int j = 0; j < m; ++j)
        {
            if(s[i][j]=='.' && !vis[i][j])
            {
                c = 0;
                dfs(i,j);
                que.push(c);
            }
        }
    }
    int sum =0, ans = 0;
    while(!que.empty() && sum < h){
        int top = que.top();
        que.pop();
        sum += top;
        ++ans;
    }
    cout << ans << endl;
    return 0;
}

problem E:
给出一个n个点m条边的DAG,( 1 < n , m <= 1e6)每个点u有一个点权c[u], 每条边无边权,人从第0个点出发在DAG走完一圈,在每一个点都可以选择获得这个点权c[u], 但获得的点权和其第几次获取有关,第k次选择获得该点权c[u]的 1 / 2 ^(k - 1). 求人的可获得的最大点权 。

solution:
首先贪心(wa) , 正解:动态规划。
思路:每个点都可以选和不选。二维dp数组。dp[u][0], dp[u][1]表示该点的点权不选和选。
然后将图进行拓扑排序,在逆拓扑序列下利用如下动态方程:

dp[v][0] = max(dp[v][0], max(dp[u][0], dp[u][1]));
dp[v][1] = max(dp[v][1], max(dp[u][0] / 2.0 + (double)c[v], dp[u][1] / 2.0 + (double)c[v]));

其中u是v在逆向拓扑排序中的父亲结点。
最终答案是max(dp[0][0], dp[0][1])
算法时间复杂度:O(n + m)

AC code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 10;
struct edge{
    int to, next;
}edge[maxn], invedge[maxn];
int head[maxn], tot, invhead[maxn], invtot;
int ind[maxn];
int c[maxn];
int topv[maxn];
double dp[maxn][2];
void init(int n){
    tot = invtot = 0;
    for(int i = 0; i <= n; ++i){
        head[i] = invhead[i] = -1;
        ind[i] = 0;
        dp[i][0] = dp[i][1] = 0.0;
    }
}

inline void addedge(const int u, const int v){
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
    invedge[invtot].to = u;
    invedge[invtot].next = invhead[v];
    invhead[v] = invtot++;
}

void solve(int n){
    int cnt = 0;
    std::queue<int>q;
    for(int i = 0; i < n; ++i){
        if(!ind[i]){
            q.push(i);
            topv[cnt++] = i;
        }
        dp[i][1] = c[i];
    }
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; ~i; i = edge[i].next){
            int v = edge[i].to;
            if(--ind[v] == 0){
                q.push(v);
                topv[cnt++] = v;
            }
        }
    }
    for(int i = cnt - 1; i >= 0; --i){
        int u = topv[i];
        for(int j = invhead[u]; ~j; j = invedge[j].next){
            int v = invedge[j].to;
            dp[v][0] = max(dp[v][0], max(dp[u][0], dp[u][1]));
            dp[v][1] = max(dp[v][1], max(dp[u][0] / 2.0 + (double)c[v], dp[u][1] / 2.0 + (double)c[v]));
        }
    }
    double ans = max(dp[0][0], dp[0][1]);
    printf("%.6lf\n", ans);
}

int main(){
    int n, m;
    scanf("%d%d",&n, &m);
    for(int i = 0; i < n; ++i){
        scanf("%d", &c[i]);
    }
    init(n);
    int u,v;
    for(int i = 0; i < m; ++i){
        scanf("%d%d",&u, &v);
        addedge(u, v);
        ++ind[v];
    }
    solve(n);
    return 0;
}

problem H:
给出k个形如ACxHy的化学分子合成形如AnHmCm的化学分子,问最多可以合成多少个。
solution:
简单贪心,按照题意模拟即可。

AC code:

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 3000;
char s[maxn];
int k;
long long a[30], b[30];
long long c[30];
bool checkup(const char c){
    if(c >= 'A' && c <= 'Z') return true;
    return false;
}
bool checknum(const char c){
    if(c >= '0' && c <= '9') return true;
    return false;
}
void getinit(char *s, long long *a){
    memset(a, 0, sizeof(a));
    long long num = 0;
    bool ok = true;
    char c = a[0];
    int n = strlen(s);
    for(int i = 0; i < n; ++i){
        if(checkup(s[i]) && ( (i + 1 < n && checkup(s[i + 1]) ) || i == n - 1)){
            ++a[s[i] - 'A'];
        } else if(checkup(s[i]) && ( i + 1 < n &&checknum(s[i + 1]))){
            c = s[i];
        } else if(checknum(s[i]) && ((i + 1 < n && checkup(s[i + 1]) ) || i == n - 1)){
            num = num * 10 + (s[i] - '0');
            a[c - 'A'] += num;
            num = 0;
        } else{
            num = num * 10 + (s[i] - '0');
        }
    }
}
int main(){
    scanf("%s%d",s, &k);
    getinit(s, a);
    scanf("%s",s);
    getinit(s, b);
    memset(c, 0, sizeof(c));
    for(int i = 0; i < 30; ++i){
        if(a[i] && b[i]){
            //cout << a[i] << " " << b[i] << endl;
            c[i] = (1LL * a[i] * k) / b[i];
        }
    }
    long long maxx = inf;
    for(int i = 0; i < 30; ++i){
        if(b[i]){
            maxx = min(c[i], maxx);
        }
    }
    if(maxx == inf){
        cout << 0 << endl;
    } else{
        cout << maxx << endl;
    }
    return 0;
}

problem I
给定一个字符串,将其切块成最多部分的回文串。
eg:013189301 spilt: 01|3|189|3|01 -> k = 5
solution:
从两边设置指针贪心选取最小的相等部分即可。 特判中间相等只有一个字符串和中间有一部分没有计算的情况。
计算相等方法,字符串哈希预处理后便是O(1),下面我采用了自然溢出的方法。

AC code:

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn = 1e6 + 10;
const int p = 11;
char s[maxn];
ull pw[maxn];
ull Hash[maxn];
int main(){
    while(~scanf("%s", s)){
        Hash[0] = 0; 
        pw[0] = 1;
        int len = strlen(s);
        for(int i = 1; i <= len; ++i){
            Hash[i] = Hash[i - 1] * p + (s[i - 1] - '0');
            pw[i] = pw[i - 1] * p;
        }
        ull ans = 0;
        int mid = (len % 2 == 0) ? (len / 2) : (len / 2 + 1);
        int st = 0, ed = 1;
        for( ; ed <= mid; ++ed){
            if((Hash[ed] - Hash[st] * pw[ed - st]) == (Hash[len - st] - Hash[len - ed] * pw[ed - st])){
                if(ed != len - st){
                    ans += 2;
                    st = ed;
                } else{
                    ++ans;
                }
            }
        }
        if(len - 2 * st > 1 || ans == 0){
            ++ans;
        }
        cout << ans << endl;
    }
    return 0;
}

problem J:
给出n(<1000)个长度为k(<10)的字符串, 字符串i和j的不同度就是i和j的之间的点权。然后求一颗最小生成树。(暖场签到,太久没写并查集,写错了gg)

AC code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000 + 5;
const int maxm = maxn * maxn;

int fa[maxn];
int Find(int x){
    return (x == fa[x]) ? x : (fa[x] = Find(fa[x]));
}

bool Union(int x, int y){
    x = Find(x), y = Find(y);
    if(x != y){
        fa[y] = x;
        return true;
    }
    return false;
}

struct Edge{
    int from, to, next, w;
    bool operator < (const Edge &b){
        return w < b.w;
    }
}edge[maxm];
int head[maxn], tot;

inline void addedge(const int u, const int v, const int w){
    edge[tot].from = u;
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

void init(){
    tot = 0;
    memset(head, -1, sizeof(head));
    for(int i = 0; i < maxn; ++i) fa[i] = i;
}

vector<pair<int,int> >vec;
void Krukal(int n){
    sort(edge, edge + tot);
    long long sum = 0;
    if(vec.size() > 0){
        vec.clear();
    }
    int cnt = 0;
    for(int i = 0; i < tot; ++i){
        int u = edge[i].from,  v = edge[i].to;
        if(Union(u, v)){
            ++cnt;
            sum += edge[i].w;
            vec.push_back(make_pair(u, v));
            if(cnt == n - 1) break ;
        }
    }
    cout << sum << endl;
    for(int i = 0; i < vec.size(); ++i){
        printf("%d %d\n", vec[i].first, vec[i].second);
    }
}

char s[maxn][11];
int getw(const char *a, const char *b){
    int w = 0;
    for(int i = 0; a[i]; ++i){
        if(a[i] != b[i]) ++w;
    }
    return w;
}

int main(){
    init();
    int n, k;
    scanf("%d%d", &n, &k);
    for(int i = 0; i < n; ++i){
        scanf("%s", s[i]);
    }
    for(int i = 0; i < n; ++i){
        for(int j = i + 1; j < n; ++j){
            int w = getw(s[i], s[j]);
            //cout << i << " " << j << " " << w << endl;
            addedge(i, j, w);
        }
    }
    Krukal(n);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值