Petrozavodsk WinterTraining 2015

PetrozavodskWinterTraining2015

A - Three Servers

题目描述:有\(n\)个数,将这\(n\)个数分成\(3\)堆,使得\(3\)堆中和的最大值减最小值最小,求方案。

solution
\(f[i][j]\)表示第二堆减第一堆等于\(i\),第三堆减第二堆等于\(j\)的方案。由于数字比较小,所以可以定\(-100 \leq i, j \leq 100\),然后将读入数据随机排序,做\(10\)次就可以过了。

时间复杂度:\(O(能过)\)

F - Empty Vessels

题目描述:有\(n\)个水杯,每个水杯的容量为\(a_i\),现在有\(3\)种操作:

  1. 将某个水杯装满水
  2. 将某个水杯里的水倒掉
  3. 将某个水杯里的水倒到另一个水杯里,直到一个水杯满或一个水杯空
    给定一个值\(m\),问是否通过若干次操作之后某个水杯里的水体积为\(m\),求出方案。

solution
显然,\(m\)一定要是\(a_i\)的最大公约数的倍数,并且\(m\)要小于\(a_i\)的最大值。
然后将最大的水杯看做剩余系,所有水杯往最大的水杯倒水,相当于水量模最大水杯容量,然后像完全背包那样做即可。

时间复杂度:\(O(最大容量*n)\)

G - Maximum Product

solution

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int maxn=30;

LL A, B;
int a[maxn], b[maxn], now[maxn];

void calc(LL num, int *a)
{
    if (num==0) a[a[0]=1]=0;
    a[0]=0;
    while (num)
    {
        a[++a[0]]=num%10;
        num/=10;
    }
}
bool check()
{
    if (now[0]!=a[0]) return now[0]>a[0];
    for (int i=now[0]; i; --i)
        if (now[i]!=a[i]) return now[i]>a[i];
    return true;
}
void solve()
{
    scanf("%lld%lld", &A, &B);
    calc(A, a);
    calc(B, b);

    LL answer=1, ans=B;
    for (int i=1; i<=b[0]; ++i) answer*=b[i];

    for (int i=b[0]; i; --i)
    {
        for (int j=b[0]; j>i; --j) now[j]=b[j];
        now[i]=b[i]-1;
        for (int j=i-1; j; --j) now[j]=9;
        for (int j=i; j<b[0]; ++j)
            if (now[j]<0)
            {
                now[j]+=10;
                now[j+1]--;
            }
            else break;
        now[0]=b[0];
        if (now[0]>1 && now[now[0]]==0) --now[0];
        if (check())
        {
            LL s=1;
            for (int j=1; j<=now[0]; ++j) s*=now[j];
            if (s>answer)
            {
                answer=s;
                ans=0;
                for (int j=now[0]; j; --j) ans=ans*10+now[j];
            }
        }
    }
    printf("%lld\n", ans);
}
int main()
{
    solve();
    return 0;
}

H - Biathlon 2.0

题目描述:分别给定\(n, m\)个向量,对于\(n\)里面的每个向量,在\(m\)中找一个向量,使得两个向量的点乘最小。

solution
根据点乘的意义,相当于是一个向量在另一个向量的投影,因此可以想到只有\(m\)个向量形成的下凸壳,在下凸壳上的点才是答案,将\(n\)个向量极角排序,然后每个向量对应的答案在下凸壳上是单调的。

时间复杂度:\(O(nlogn)\)

I - Archaeological Research

题目描述:有一个未知序列\(a_i\),给出一些数据,第\(i\)行第\(j\)个数据表示某个数在序列的第\(i+1\)个数后面(包括\(i+1\))第一次出现的位置是\(j\),找出\(a_i\)的可行解中字典序最小的一个。

solution
题目还可以用另一种方式表达:第\(i\)行第\(j\)个数据表示某个数在序列的第\(i+1\)位到第\(j-1\)位没出现过,进而可以求出\(a_i\)\([l_i, i-1]\)没出现过,然后只要贪心地从左到右放数,每次取\([1, l_i-1]\)中最小的数,如果\(l_i=1\),那么新开一个数字给\(a_i\)

时间复杂度:\(O(nlogn)\)

J - Sockets

题目描述:有一个电源(只有一个插座),\(n\)个排插,分别有\(a_i\)个插座,每个排插可以直接接电源或者插到别的排插上,有\(m\)部手机需要同时充电,每部手机规定到电源之间最多能有\(b_i\)个排插,问最多能有多少部手机同时充电。

solution
将排插按\(a_i\)从大到小排序,手机按\(b_i\)从小到大排序,二分答案,先满足\(b_i\)较小的,其它位置插排插。

时间复杂度:\(O(nlogn)\)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int maxn=int(2e5)+100;

int n, m;
int a[maxn], b[maxn];


void read()
{
    scanf("%d%d", &n, &m);
    for (int i=1; i<=n; ++i) scanf("%d", &a[i]);
    for (int i=1; i<=m; ++i) scanf("%d", &b[i]);
}
bool check(int num)
{
    LL rest=1;
    for (int i=1, level=0; i<=n && num; ++level)
    {
        int k=num;
        while (k && b[k]==level) --k;
        if (num-k>rest) return false;
        LL tmp=(rest-=(num-k));
        num=k;
        while (i<=n && (tmp--)) rest+=a[i++]-1;
    }
    return rest>=num;
}
void solve()
{
    sort(a+1, a+1+n, greater<int>());
    sort(b+1, b+1+m, greater<int>());

    int L=0, R=m+1;
    while (L+1<R)
    {
        int mid=(L+R)>>1;
        if (check(mid)) L=mid;
        else R=mid;
    }
    printf("%d\n", L);
}
int main()
{
    read();
    solve();
    return 0;
}

K - Toll Roads

题目描述:给定一棵树,边权为\(1\),现在可以选择一条长度不超过\(m\)的链,将链上的边权全改为\(0\),使得树的直径最小,求方案。

solution
思路挺好想的,就是枚举一端作为树根,然后类似树形\(dp\)算出当另一端某个点时,树的直径是多少。

时间复杂度:\(O(n^2)\)

#include <bits/stdc++.h>
using namespace std;

const int maxn=5010;

int n, m, root;
vector<int> out[maxn];
int idx[maxn];
pair<int, int> le[maxn], ri[maxn];
int radle[maxn], radri[maxn];
int fa[maxn], f[maxn], g[maxn], h[maxn];
int rad[maxn], deep[maxn];
pair<int, int> ans, solu;

void read()
{
    scanf("%d%d", &n, &m);
    for (int i=0; i<n; ++i) out[i].clear();
    for (int i=1; i<n; ++i)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        out[u].push_back(v);
        out[v].push_back(u);
    }
}
void dfs1(int cur, int _fa)
{
    rad[cur]=0;
    f[cur]=0;
    fa[cur]=_fa;
    if (_fa==-1) deep[cur]=0;
    else deep[cur]=deep[_fa]+1;
    for (auto &to:out[cur])
    if (to!=_fa)
    {
        dfs1(to, cur);
        rad[cur]=max(rad[cur], max(rad[to], f[cur]+f[to]+1));
        f[cur]=max(f[cur], f[to]+1);
    }
}
void dfs2(int cur, int past, int chain)
{
    if (deep[cur]>m) return;
    int s=max(past, max(rad[cur], chain+f[cur]));
    if (make_pair(s, deep[cur])<ans)
    {
        ans=make_pair(s, deep[cur]);
        solu=make_pair(root, cur);
    }
    int cnt=0;
    for (auto &to:out[cur])
        if (to!=fa[cur]) idx[++cnt]=to;
    le[1]=make_pair(0, 0);
    radle[1]=0;
    for (int i=2; i<=cnt; ++i)
    {
        le[i]=le[i-1];
        radle[i]=max(radle[i-1], rad[idx[i-1]]);
        if (f[idx[i-1]]+1>le[i].first)
        {
            le[i].second=le[i].first;
            le[i].first=f[idx[i-1]]+1;
        }
        else le[i].second=max(le[i].second, f[idx[i-1]]+1);
    }
    ri[cnt]=make_pair(0, 0);
    radri[cnt]=0;
    for (int i=cnt-1; i>0; --i)
    {
        ri[i]=ri[i+1];
        radri[i]=max(radri[i+1], rad[idx[i+1]]);
        if (f[idx[i+1]]+1>ri[i].first)
        {
            ri[i].second=ri[i].first;
            ri[i].first=f[idx[i+1]]+1;
        }
        else ri[i].second=max(ri[i].second, f[idx[i+1]]+1);
    }
    for (int i=1; i<=cnt; ++i)
    {
        g[idx[i]]=max(le[i].first, ri[i].first);
        h[idx[i]]=max(le[i].first+ri[i].first, max(le[i].first+le[i].second, ri[i].first+ri[i].second));
        h[idx[i]]=max(h[idx[i]], max(radle[i], radri[i]));
    }
    for (auto &to:out[cur])
        if (to!=fa[cur]) dfs2(to, max(past, max(h[to], chain+g[to])), max(chain, g[to]));
}
void solve()
{
    ans=make_pair(n*2, 0);
    for (int i=0; i<n; ++i)
    {
        root=i;
        dfs1(i, -1);
        dfs2(i, 0, 0);
    }
    printf("%d\n%d\n", ans.first, ans.second);
    if (ans.second) printf("%d %d\n", solu.first, solu.second);
}
int main()
{
    read();
    solve();
    return 0;
}

转载于:https://www.cnblogs.com/GerynOhenz/p/9892634.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值