Codeforces Round #367 (Div. 2)---水题 | dp | 01字典树

A.Beru-taxi

水题:有一个人站在(sx,sy)的位置,有n辆出租车,正向这个人匀速赶来,每个出租车的位置是(xi, yi) 速度是 Vi;求人最少需要等的时间;

单间循环即可;

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<math.h>
#include<vector>
using namespace std;
#define INF 0x3f3f3f3f
#define N 102550
#define PI 4*atan(1.0)
#define mod 100000001
#define met(a, b) memset(a, b, sizeof(a))
typedef long long LL;

int main()
{
    int sx, sy, n;
    double ans = INF;
    scanf("%d %d %d", &sx, &sy, &n);
    for(int i=1; i<=n; i++)
    {
        int x, y, v;
        scanf("%d %d %d", &x, &y, &v);
        double d = sqrt((x-sx)*(x-sx)+(y-sy)*(y-sy));
        ans = min(ans, d/v);
    }
    printf("%.7f
", ans);
    return 0;
}
View Code

B.Interesting drink

 有n个物品,每个物品为ai元,某人每天有x元钱,求每天他能买几种的物品;

排序二分即可;

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<math.h>
#include<vector>
using namespace std;
#define INF 0x3f3f3f3f
#define N 102550
#define PI 4*atan(1.0)
#define mod 100000001
#define met(a, b) memset(a, b, sizeof(a))
typedef long long LL;

int a[N];

int main()
{
    int n, m, num;
    while(scanf("%d", &n)!=EOF)
    {
        for(int i=0; i<n; i++)
            scanf("%d", &a[i]);
        sort(a, a+n);
        scanf("%d", &m);
        for(int i=0; i<m; i++)
        {
            scanf("%d", &num);
            int ans = upper_bound(a, a+n, num)-a;
            printf("%d
", ans);
        }
    }
    return 0;
}
View Code

C.Hard problem

 简单dp:给你n个字符串,然后问这n个字符串要想变成字典序排列的字符串所需的代价是多少,每个字符串可以倒置,(倒置串 i 的代价是a[i]),或者不变;

可以用dp[i][0]表示前i个字符串已经是按字典序排列的,并且第i个字符串不倒置的最小代价;用dp[i][0]表示前i个字符串已经是按字典序排列的,并且第i个字符串倒置的最小代价;

所以公式很容易写出来;

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<math.h>
#include<vector>
using namespace std;
#define INF 0x3f3f3f3f
#define oo 1000000000000000
#define N 102550
#define PI 4*atan(1.0)
#define mod 100000001
#define met(a, b) memset(a, b, sizeof(a))
typedef long long LL;

int n;
LL a[N], dp[N][2];

char s[4][N];

void Strrev(char str[])
{
    int len = strlen(str);
    for(int i=0, j=len-1; i<j; i++, j--)
        swap(str[i], str[j]);
}

int main()
{
    scanf("%d", &n);
    for(int i=1; i<=n; i++)
    {
        scanf("%I64d", &a[i]);
        dp[i][0] = dp[i][1] = oo;
    }

    int p = 0;

    dp[1][0] = 0; dp[1][1] = a[1];

    for(int i=1; i<=n; i++)
    {
        p = p^1;
        scanf("%s", s[p]);
        if(i == 1) continue;

        strcpy(s[p+2], s[p]);
        Strrev(s[p+2]);
        strcpy(s[(p^1)+2], s[p^1]);
        Strrev(s[(p^1)+2]);

        if(strcmp(s[p], s[p^1]) >= 0)
            dp[i][0] = min(dp[i][0], dp[i-1][0]);
        if(strcmp(s[p], s[(p^1)+2]) >= 0)
            dp[i][0] = min(dp[i][0], dp[i-1][1]);
        if(strcmp(s[p+2], s[p^1]) >= 0)
            dp[i][1] =  min(dp[i][1], dp[i-1][0] + a[i]);
        if( strcmp(s[p+2], s[(p^1)+2]) >= 0)
            dp[i][1] = min(dp[i][1], dp[i-1][1] + a[i]);
    }
    LL ans = min(dp[n][0], dp[n][1]);
    if(ans == oo)ans = -1;
    printf("%I64d
", ans);
    return 0;
}
View Code

D.Vasiliy's Multiset

01字典树:有一个里面初始值只有 0 的集合,执行 n 个操作,每个操作有三种可能:

+ x把x添加到集合中去;

- X把x从集合中删去一个;保证集和中一定有x;

? x从集合中找到一个数y,使得x异或y最大化;

对于每个?操作输出对应的最大的值;

01字典树模板:把十进制数转化为二进制数,通过前补零的方法构成32位,然后从前到后依次添加到字典树中去;

在查找时,每次尽量查找当前位相反的数的那个位置, 因为异或是不同为1的;

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stdio.h>
#include<math.h>
#include<vector>
using namespace std;
#define INF 0x3f3f3f3f
#define oo 1000000000000000
#define N 102550
#define PI 4*atan(1.0)
#define mod 100000001
#define met(a, b) memset(a, b, sizeof(a))
typedef long long LL;

struct node
{
    int sum, num;///sum表示有多少个数经过当前这个节点;num为从根节点到叶子节点表示的数;
    node *Next[2];///指向下一层的节点;
};

void Add(node *head, int x, int cnt)///把x对应的二进制01插入到字典树中去;共32位,前补零形式;
{
    node *p = head;
    for(int i=31; i>=0; i--)///这一点不太理解位运算的,但是相当于是把x转换成二进制,然后插入其中;
    {
        int k = (x>>i)&1;
        if(p->Next[k] == NULL)///当不存在当前节点时,开创新节点,并连接在对应位置;
        {
            node *q = new node();
            p->Next[k] = q;
        }
        p = p->Next[k];///接着往下走;
        p->sum += cnt;///更新节点经过数;
    }
    p->num = x;///结束的时候更新叶子节点对应的数;
}

int Find(node *head, int x)
{
    node *p = head;
    for(int i=31; i>=0; i--)
    {
        int k = (x>>i)&1;
        if(p->Next[k^1] != NULL && p->Next[k^1]->sum > 0)///尽量找与k不同的;应为异或是不同为1,所以要想最大,就要尽量不同;
            p = p->Next[k^1];
        else if(p->Next[k] != NULL && p->Next[k]->sum > 0)
            p = p->Next[k];
    }
    return (p->num)^x;///返回对应的最大结果;
}

int main()
{
    int n, x; char ch;

    scanf("%d", &n);

    node *head = new node();

    Add(head, 0, 1);

    for(int i=1; i<=n; i++)
    {
        scanf(" %c %d", &ch, &x);
        if(ch == '+')
            Add(head, x, 1);
        else if(ch == '-')///删除,相当于更新sum即可;
            Add(head, x, -1);
        else
        {
            int ans = Find(head, x);
            printf("%d
", ans);
        }
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/zhengguiping--9876/p/5804739.html