2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 2)【solved:12 / 13】

来源:互联网 发布:反转链表 非递归 java 编辑:程序博客网 时间:2024/06/07 11:38

A.Alphabet(LIS变形)

题意:

  给你一个字符串,问你最少添加几个字符使得它能够存在一个子序列“abcdefg...xyz”。

数据:

  长度不超过50。

思路:

  我们枚举每一个字符ch作为起点,求一个LIS,是不是就得到了一个“abcdefg...xyz”的子串,然后填补上ch之前缺失的部分是不是就行了。答案就是min{26-len(LIS)}。复杂度O(n * n lg n)。

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 1e5 + 5;int main(){    int a[55];    char s[55];    gets(s);    int len = strlen(s);    int ans = 0x3f3f3f3f;    for(int i = 0; i < len;i++)    {        a[i] = s[i] - '0';    }    int dp[55];    for(int i = 0; i < len; i++)    {        memset(dp, 0x3f3f3f3f, sizeof(dp));        for(int j = i; j < len; j++)        {            int pos = lower_bound(dp, dp + len, a[j]) - dp;            dp[pos] = a[j];        }        int temp = 26 - (lower_bound(dp, dp + len, 0x3f3f3f3f) - dp);        ans = min(ans, temp);    }    printf("%d\n", ans);    return 0;}


 

 

B.Barbells(暴力三进制枚举)

题意:

  给你n个杠铃,m个杠铃的片,问你能组合出哪些杠铃(前提:左右杠铃片的质量相等),求出distinct的杠铃的重量。

数据:

  n和m不超过14,杠铃和杠铃片的质量不超过1e8.

思路:

  每个杠铃片有三种状态,放在左边,放在右边,不放。我们暴力枚举一下就行了,非常简单。O(3^m)。

#include<bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 20;LL b[maxn], p[maxn];int main(){    int n, m;    scanf("%d%d", &n, &m);    int stateMax = 1;    for(int i = 0; i < m; i++)    {        stateMax *= 3;    }    for(int i = 0; i < n; i++)  scanf("%lld", &b[i]);    for(int i = 0; i < m; i++)  scanf("%lld", &p[i]);    sort(p, p + m);    set<LL>ans;    for(int S = 0; S < stateMax; S++)    {        int state = S;        LL left = 0, right = 0;        for(int i = 0; i < m; i++)        {            int f = state % 3;            state /= 3;            if(f == 0)  left += p[i];            else if(f == 1) right += p[i];            else if(f == 3) continue;        }        if(left != right)   continue;        for(int i = 0; i < n; i++)        {            ans.insert(b[i] + left + right);        }    }    for(auto o : ans)        cout << o <<endl;    return 0;}



C.Buggy Robot

题意:

  大概就是你写一个up,down,left,right的指令,操控小机器人走出迷宫,然后如果机器人遇到的这条指令,是让它走到障碍物上的,它会跳过这条指令。如果机器人到达了终点,则所有剩余指令失效。你可以通过增加和删除指令,来使得机器人走到中间,问你至少需要修改几个指令。

数据:

  迷宫的大小50*50。指令的长度是1-50。

思路:

   大概就是一个spfa吧。dp[i][j][k]:(i, j)这个位置 下一步执行原指令的第k条的时候 需要增加的最少指令数。然后转移状态,如果这个状态和指令相同,idx++。

#include <bits/stdc++.h>using namespace std;const int INF = 0x3f3f3f3f;struct node{    int x, y, idx;};int n, m, sx, sy;int dp[55][55][55];//dp[i][j][k]:(i, j)这个位置 下一步执行原指令的第k条的时候 需要增加的最少指令数bool vis[55][55][55];char ma[55][55];char cmd[55];char dir[129];int dx[] = {-1, 1, 0, 0};int dy[] = {0, 0, -1, 1};int spfa(){    int ret = INF;    memset(dp, INF, sizeof(dp));    queue<node>que;    dp[sx][sy][0] = 0;    que.push({sx, sy, 0});    while(que.size())    {        node cur = que.front();que.pop();        vis[cur.x][cur.y][cur.idx] = 0;        if(ma[cur.x][cur.y] == 'E')        {            ret = min(ret, dp[cur.x][cur.y][cur.idx]);            continue;        }        for(int i = 0; i < 4; i++)        {            int fx = cur.x + dx[i], fy = cur.y + dy[i];            //如果越界或者走到了障碍物 那么skip这条指令。            if (fx < 1 || fx > n || fy < 1 || fy > m || ma[fx][fy] == '#')            {                if (dir[cmd[cur.idx]] == i)                {                    if(dp[cur.x][cur.y][cur.idx] < dp[cur.x][cur.y][cur.idx + 1])                    {                        dp[cur.x][cur.y][cur.idx + 1] = dp[cur.x][cur.y][cur.idx];                        if (!vis[cur.x][cur.y][cur.idx + 1])                        {                            vis[cur.x][cur.y][cur.idx + 1] = 1;                            que.push({cur.x, cur.y, cur.idx + 1});                        }                    }                }                continue;            }            int ans = dp[cur.x][cur.y][cur.idx];            int fidx = cur.idx;            if (dir[cmd[cur.idx]] == i)                fidx++;            else                ans++;            if (ans < dp[fx][fy][fidx])            {                dp[fx][fy][fidx]= ans;                if (!vis[fx][fy][fidx])                {                    vis[fx][fy][fidx] = 1;                    que.push({fx, fy, fidx});                }            }        }    }    return ret;}int main(){    memset(dir, -1, sizeof(dir));    dir['U'] = 0;    dir['D'] = 1;    dir['L'] = 2;    dir['R'] = 3;    scanf("%d%d", &n, &m);    for(int i = 1; i <= n; i++)    {        scanf("%s", ma[i] + 1);        for(int j = 1; j <= m; j++)        {            if(ma[i][j] == 'R')    sx = i, sy = j;        }    }    scanf("%s", cmd);    cout << spfa() << endl;    return 0;}


 

 

D.Cameras(思维?)

题意:

  数轴上1-n,你已经在其中k个位置有了标记,问你至少需要添加几个标记,使得任意连续r个位置,都至少有diff = 2个标记。

数据:

  2<=n<=100000, 0 <= k <= n, 2 <= r <= n;

思路:

  先把[1,r]区间填充的满足条件,然后每次挪动其实只要更改第一个和最后一个就行了,你们觉不觉得其实这个diff这个不是2,是3是4是5都行呀,只要<=r。这题可以随便改啊,这样O(n)就够了。

#include<bits/stdc++.h>using namespace std;const int diff = 2;int vis[100000 + 5];int main(){    int n, k, r;    scanf("%d%d%d", &n, &k, &r);    for(int i = 0; i < k; i++)    {        int x;        scanf("%d", &x);        vis[x] = 1;    }    int num = 0, ans = 0;    for(int i = 1; i <= r; i++)    {        num += vis[i];    }    ans += max(0, diff - num);    for(int i = r; i >= 1; i --)    {        if(num == diff)  break;        if(!vis[i]) vis[i] = 1, num++;    }    //接下来可以保证每一个进入的区间[l,r]的前一个[l-1,r-1]都是满足diff这个条件的。因为每次只挪动1,所以只需要更改最后一个。    for(int nail = r + 1; nail <= n; nail++)    {        num = num - vis[nail - r] + vis[nail];        if(num < diff)  vis[nail] = 1, ans++, num = diff;    }    printf("%d\n", ans);    return 0;}


 

E.Contest Score(前缀和水题)

题意:

  计算类似ACM的罚时呀,就是给你n个任务,然后你最多一口气能按顺序选前k个未完成的任务,然后挑最快的完成,重复至结束。问你罚时最短是多少。

数据:

  n和k不超过300,每个任务时间不超过一百万。

思路:

  水题。。维护个前缀和就行了。O(nlogn)

#include <bits/stdc++.h>using namespace std;int t[305];int main(){    int n, k;    scanf("%d%d", &n, &k);    for(int i = 0; i < n; i++)    {        scanf("%d", &t[i]);    }    priority_queue<int, vector<int>, greater<int>>que;    int cnt = 0;    while(cnt < k)  que.push(t[cnt++]);    long long sum = 0, ans = 0;    while(que.size())    {        sum += que.top();que.pop();        ans += sum;        if(cnt < n) que.push(t[cnt++]);    }    printf("%lld\n", ans);    return 0;}


 

F.Equality(....water)

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 1e5 + 5;int main(){    char s[15];    gets(s);    int a, b, c;    if(sscanf(s, "%d + %d = %d", &a, &b, &c) == 3)    {        if(a + b == c)  puts("YES");        else puts("NO");    }    return 0;}


 

 

G.Gravity(模拟)

#include <bits/stdc++.h>using namespace std;char ma[305][305];int main(){    int n, m;    scanf("%d%d", &n, &m);    for(int i = 1; i <= n; i++) scanf("%s", ma[i] + 1);    for(int i = n - 1; i >= 0; i--)    {        for(int j = 1; j <= m; j++)        {            if(ma[i][j] != 'o') continue;            int k = i;            while(k + 1 <= n && ma[k + 1][j] == '.') k++;            ma[i][j] = '.';            ma[k][j] = 'o';        }    }    for(int i = 1; i <= n; i++) printf("%s\n", ma[i] + 1);    return 0;}


 

H.Islands(dfs联通块)

题意:

  大概就是给你个图有陆地,水,陆地/水三种状态,然后问你最少有几块陆地,联通的算一块。

数据:

   50 * 50的图。。很小

思路:

  如果是陆地,开始dfs,dfs的过程中,只要非水就能继续。求个联通块数量就可以啦。

#include <bits/stdc++.h>using namespace std;int n, m;char ma[305][305];int vis[305][305];int dx[] = {0, 0, 1, -1};int dy[] = {1, -1, 0, 0};#define isInside(x, y)  0 <= x && x < n && 0 <= y && y < mvoid dfs(int x, int y){    vis[x][y] = 1;    for(int i = 0; i < 4; i++)    {        int fx = x + dx[i], fy = y + dy[i];        if(isInside(fx, fy) && vis[fx][fy] == 0 && ma[fx][fy] != 'W')        {            dfs(fx, fy);        }    }}int main(){    scanf("%d%d", &n, &m);    for(int i = 0; i < n; i++)    {        scanf("%s", ma[i]);    }    int ans = 0;    for(int i =0; i < n; i++)    {        for(int j = 0; j < m; j++)        {            if(vis[i][j] == 0 && ma[i][j] == 'L')            {                dfs(i, j);                ans++;            }        }    }    printf("%d\n", ans);    return 0;}


 

 

I.Mismatched Socks(雀巢原理?)

题意:

   给你n种不同的袜子的各自的数量。问你最多能配几对不同的袜子。

数据:

   n不超过1000,ki不超过1e9。

思路:

   雀巢原理?统计一下最多的袜子有几双,剩下的袜子有几双,其实就是一个雀巢原理的穿插的感觉吧。

   其实复杂度可以O(n)的,只是看看这数据好水,才1000,懒得写max了,直接用堆了。

#include <bits/stdc++.h>using namespace std;typedef long long LL;int main(){    int n;    scanf("%d", &n);    LL preSum = 0;    priority_queue<int>que;    for(int i =0; i <n ;i++)    {        int x;        scanf("%d", &x);        que.push(x);        preSum += x;    }    int maxx = que.top();    preSum -= maxx;    if(preSum >= maxx) printf("%lld\n", (preSum + maxx) / 2);    else printf("%lld\n", preSum);    return 0;}


 

 

J.Postman(贪心?)

题意:

   有一个邮差员要去n家送信,他每次只能带k封信。每一家的坐标为xi,需要送mi封信,然后邮局在0点,问你最少走多少路能送完信。

数据:

   n是1e3,k,xi,mi都是1e7.

思路:

   在纸上画一画,可以大概的发现,如果某次邮递员跨越了0点,那么和重新出发没有区别,哦不对,重新出发应该更优,能带更多的信。所以可以把问题拆分成两个独立的部分,解决正半轴以后,负半轴同理。

  正半轴怎么办呢,明显是由远及近的送,然后递减就好了。

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef pair<int, int>pii;int n,k;const int maxn = 1e5 + 5;struct node{    LL id, x, m;    bool operator < (const node &other)const    {        if(x != other.x)    return x < other.x;        if(m != other.m)    return m < other.m;        return id < other.id;    }};LL x[maxn], m[maxn];LL solve(priority_queue<node>que){    LL ret = 0;    while(que.size())    {        node cur = que.top();que.pop();        LL temp = (cur.m + k - 1) / k;//send mail        LL d = 0;        if(cur.m % k != 0)  d = k - cur.m % k;        ret += temp * cur.x * 2LL;//走的距离        while(d)        {            if(que.size() == 0)   break;            node cur = que.top();que.pop();            int del = min(d, cur.m);//这个点能送掉的信,是两者的较小值            cur.m -= del;            d -= del;            if(cur.m != 0)  que.push({cur});        }    }    return ret;}int main(){    scanf("%d%d", &n, &k);    for(int i = 0; i < n; i++)    scanf("%lld%lld", &x[i], &m[i]);    priority_queue<node>que1, que2;    for(int i = 0; i < n; i++)    {        if(x[i] > 0)    que1.push({i, x[i], m[i]});        else que2.push({i, -x[i], m[i]});    }    LL ans = 0;    ans += solve(que1);    ans += solve(que2);    cout << ans << endl;    return 0;}


 

 

K.Six Sides

题意:

  有俩骰子,六个面的,然后告诉你他们各自面上分别是多少数值。一人扔一次,点数大的赢。如果相同,再扔一次,直到分出胜负。问你第一个人赢的概率。

数据:

   

思路:

   。。推个公式就完。p是第一个人赢的概率,q是平局的概率,那么ans = sigma{p * q ^ k} 0 <= k。无穷等比数列。

#include <bits/stdc++.h>using namespace std;int main(){    int a[6], b[6];    for(int i = 0; i < 6; i++)    {        scanf("%d", &a[i]);    }    for(int i = 0; i < 6; i++)    {        scanf("%d", &b[i]);    }    int x = 0, y = 0;    for(int i = 0; i < 6;i ++)    {        for(int j = 0; j < 6; j++)        {            if(a[i] > b[j]) x++;            else if(a[i] == b[j])   y++;        }    }    double p = x / 36.0, q = y / 36.0;//p为player1胜的概率  q为平局概率    printf("%.5f\n", p / (1.0 - q));    return 0;}


 

L.Three Square

emmm....不会做。但是,我有队友啊!2333

M.Zigzag(dp)

题意:

   问你形成一个上升下降交替进行的子串,最长是多少。

数据:

   n不超过50。

思路:

  不明白为什么数据这么水。。。n才50.

  定义dp[i][k]:到第i个数字的时候,最后一步是递增的最长子串长度为dp[i][0],最后一步是递减的最长子串长度为dp[i][1]。

  wa点:!!!相等情况!!!不能连啊- - 严格的递增递减

#include <bits/stdc++.h>using namespace std;int a[55];int dp[55][2];int main(){    int n;    scanf("%d", &n);    for(int i = 1; i <=n; i++)    {        scanf("%d", &a[i]);    }    dp[1][0] = dp[1][1] = 1;    for(int i = 2; i <= n; i++)    {        for(int j = 0; j < i; j++)        {            if(a[i] > a[j])                dp[i][0] = max(dp[i][0], dp[j][1] + 1);            else if(a[i] < a[j])                dp[i][1] = max(dp[i][1], dp[j][0] + 1);        }    }    printf("%d\n", max(dp[n][0], dp[n][1]));    return 0;}




0 0