高考集训Day3

A. Watching Fireworks is Fun

题目描述

一个节日将在一个镇的主街道上举行。 街道被分为n段,从左到右编号为1到n,每个相邻部分之间的距离为1。

节日里在主街道有m个烟花要放,第i(1≤i≤m)次烟花将在ti时在ai段燃放。 如果您在第i次发射时处于x(1≤x≤n),您将获得幸福值bi - | ai - x | (请注意,幸福价值可能是负值)

您可以以单位时间间隔移动长度为d个长度单位,但禁止离开主要街道。 此外,在初始时刻(时间等于1)你可以在街道的任意部分,并希望最大化从观看烟花获得的幸福总和。 找到最大的总幸福。

请注意,两个或多个烟花可以同时发射

输入格式

第一行包含三个整数n, m, d ()

接下来m行每行包含三个整数ai, bi, ti ()。第 i 行描述第 i 次烟花发射

确保满足条件ti≤ti + 1(1≤i<m)

输出格式

输出一个整数,表示观看所有烟花获得的最大幸福总和

样例

样例输入

样例输入1:
50 3 1
49 1 1
26 1 4
6 1 10

样例输入2:
10 2 1
1 1000 4
9 1000 4

样例输出

样例输出1:
-31

样例输出2:
1992

CODE
如果用正常版的滚动数组当然也可以,需要注意控制数组的变量一定完全是全局变量,就是不能for(int i=1,w=0;...(w控制数组)
/*
emmm...又是一道不会改的题

经过Day2和Day3的考试,我发现我根本就没有真正地学会
怎么用单调队列或斜率优化dp 每用每错
抄起来倒是简单呵呵

---code is from WintersRain
%%%
*/
#include <bits/stdc++.h>
//#define int long long

using namespace std;

typedef long long ll;
const int maxn = 1e6 + 2;
const int mod = 998244353;
const ll INF = 1152921504606846976;//这个数不错,我要了!

int n, m, d, que[maxn];
ll ans, tmp[maxn], dp[maxn];

struct node
{
    int a, b, t;
}fire[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

//啊!!!我一定要搞定单调队列,不信这个邪了。。
//只能说看懂了,但我能不能自己写的出来还是个问题

signed main()
{
    freopen("fire.in", "r", stdin);
    freopen("fire.out", "w", stdout);

    n = read(); m = read(); d = read();
    for(int i=1; i<=m; i++)
    {
        fire[i].a = read(); fire[i].b = read(); fire[i].t = read();
    }
    for(int i=1; i<=m; i++)
    {
        memcpy(tmp, dp, sizeof(dp));//我第一次看见这么赋值的
        //tmp和dp一起组成滚动数组,tmp是上一个烟花
        //滚动之前dp[i][j]表示在位置i烟花j的时候获得的最优的欢乐值
        //Dp[i][j]=Dp[x][j-1] (x 和 i 之间的差距要在时间内走的到才能转移)+ 当前时刻能够获得的价值 sum;
        if(fire[i].t == fire[i-1].t)//为什么它可以以防万一??这个式子特殊在哪儿了
        {
            for(int j=1; j<=n; j++)//留在原地
            {
                dp[j] = tmp[j] + fire[i].b - abs(fire[i].a - j);
            }
            continue;
        }
        int l = 1, r = 0;
        int k = 1;//k表示上一个烟花燃放时在哪个位置
        ll dis = (ll)(fire[i].t - fire[i-1].t) * d;//在这个距离内都可以,没说单位时间只能走d步
        //所以说我一开始连题意都没看懂
        //dis表示在两次烟花之间可以跑多远
        for(int j=1; j<=n; j++)//i燃放时位置为j
        {
            while(k <= min(dis+j, (ll)n))//。。不用判断下界是因为下界被删了
            {
                while(l <= r && tmp[k] >= tmp[que[r]]) r--;//单调递减的队列,队首是区间最大值
                que[++r] = k;
                k++;
            }
            while(l <= r && j-dis>que[l]) l++;
            dp[j] = tmp[que[l]] + fire[i].b - abs(fire[i].a - j);
        }
    }
    ans = -INF;
    for(int i=1; i<=n; i++)
    {
        ans = max(ans, dp[i]);
    }
    printf("%lld", ans);

    return 0;
}

B. Perform巡回演出

题目描述

Flute 市的 Phlharmoniker 乐团 2000 年准备到 Harp 市做一次大型演出 , 本着普及古典音乐的目的 , 乐团指挥 L.Y.M 准备在到达 Harp 市之前先在周围一些小城市作一段时间的巡回演出 , 此后的几天里 , 音乐家们将每天搭乘一个航班从一个城市飞到另一个城市 , 最后才到达目的地 Harp 市 ( 乐团可多次在同一城市演出 ). 由于航线的费用和班次每天都在变 , 城市和城市之间都有一份循环的航班表 , 每一时间 , 每一方向 , 航班表循环的周期都可能不同 . 现要求寻找一张花费费用最小的演出表 .

输入格式

输入文件包括若干个场景 . 每个场景的描述由一对整数 n(2<=n<=10) 和 k(1<=k<=1000) 开始 , 音乐家们要在这 n 个城市作巡回演出 , 城市用 1..n 标号 , 其中 1 是起点 Flute 市 ,n 是终点 Harp 市 , 接下来有 n*(n-1) 份航班表 , 一份航班表一行 , 描述每对城市之间的航线和价格 , 第一组 n-1 份航班表对应从城市 1 到其他城市 (2,3,...n) 的航班 , 接下的 n-1 行是从城市 2 到其他城市 (1,3,4...n) 的航班 , 如此下去 . 每份航班又一个整数 d(1<=d<=30) 开始 , 表示航班表循环的周期 , 接下来的 d 个非负整数表示 1,2...d 天对应的两个城市的航班的价格 , 价格为零表示那天两个城市之间没有航班 . 例如 "3 75 0 80" 表示第一天机票价格是 75KOI, 第二天没有航班 , 第三天的机票是 80KOI, 然后循环 : 第四天又是 75KOI, 第五天没有航班 , 如此循环 . 输入文件由 n=k=0 的场景结束 .

输出格式

对每个场景如果乐团可能从城市 1 出发 , 每天都要飞往另一个城市 , 最后 ( 经过 k 天 ) 抵达城市 n, 则输出这 k 个航班价格之和的最小值 . 如果不可能存在这样的巡回演出路线 , 输出 0.

样例

样例输入

3 6
2 130 150
3 75 0 80
7 120 110 0 100 110 120 0
4 60 70 60 50
3 0 135 140
2 70 80
2 3
2 0 70
1 80
0 0

样例输出

460
0
CODE
少有的1A
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 3;
const int mod = 998244353;
const ll INF = 0x7fffffff;

int n, k, t[12][12];
ll f[1002][12];

struct node
{
    int day[31];
}c[12][12];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int main()
{
    freopen("perform.in", "r", stdin);
    freopen("perform.out", "w", stdout);

    while(1)
    {
        n = read(); k = read();
        if(n == 0 && k == 0) break;

        memset(c, 0, sizeof(c));
        memset(t, 0, sizeof(t));
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                if(j == i) continue;
                t[i][j] = read();//从i到j的周期
                for(int k=1; k<=t[i][j]; k++)
                {
                    c[i][j].day[k] = read();//从i到j第k天的票价
                    //printf("c[%d][%d].day[%d] == %d\n", i, j, k, c[i][j].day[k]);
                }
            }
        }

        memset(f, 0x3f, sizeof(f));
        f[1][1] = 0;
        //printf("n==%d k==%d\n", n, k);
        for(int i=2; i<=k+1; i++)
        {
            for(int j=1; j<=n; j++)
            {
                for(int kk=1; kk<=n; kk++)
                {
                    if(kk == j) continue;
                    //printf("i=%d j=%d k=%d\n", i, j, kk);
                    //printf("%d %d\n", i-1, t[kk][j]+1);
                    //int s = (i-1) % (t[kk][j]+1);
                    int s = i-1;
                    while(s > t[kk][j])
                    {
                        s -= t[kk][j];
                    }
                    //printf("s == %d\n", s);
                    if(c[kk][j].day[s] == 0) continue;
                    //printf("cmp: %lld   %lld\n", f[i][j], f[i-1][kk]+c[kk][j].day[s]);
                    //printf("INSERT: f[%d][%d] == %lld\n", i-1, kk, f[i-1][kk]);
                    f[i][j] = min(f[i][j], f[i-1][kk]+c[kk][j].day[s]);
                    //printf("f[%d][%d] == %lld\n", i, j, f[i][j]);
                }
            }
        }
        if(f[k+1][n] == 4557430888798830399)
        {
            printf("0"); continue;
        }
        printf("%lld\n", f[k+1][n]);
    }

    return 0;
}

C. 枪战Maf

题目描述

有  n个人,用 1~n 进行编号,每个人手里有一把手枪。一开始所有人都选定一个人瞄准(有可能瞄准自己)。然后他们按某个顺序开枪,且任意时刻只有一个人开枪。因此,对于不同的开枪顺序,最后死的人也不同。

输入格式

  • 输入人数 n < 1e6
  • 接下来  n个数,依次为每个人的 aim

输出格式

输出只有一行,共两个数,为要求最后死亡数目的最小和最大可能。

样例

样例输入

8
2 3 2 2 6 7 8 5

样例输出

3 5
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e6 + 2;
const int mod = 998244353;
const ll INF = 0x7fffffff;

int n, dfn[maxn], low[maxn], num, t, belong[maxn];
int q[maxn], cnt, ans1, ans2, du[maxn];
bool v[maxn], zi[maxn], zih[maxn];
stack<int> stk;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct node
{
    int next, to;
}a[maxn];
int len, head[maxn];

void add(int x, int y)
{
    a[++len].to = y; a[len].next = head[x];
    head[x] = len;
}

struct node2
{
    int from, to;
}b[maxn];

void tarjan(int x)
{
    dfn[x] = low[x] = ++num;
    stk.push(x);
    v[x] = 1;
    for(int i=head[x]; i; i=a[i].next)
    {
        int to = a[i].to;
        if(!dfn[to])
        {
            tarjan(to);
            low[x] = min(low[x], low[to]);
        }
        else if(v[to])
        {
            low[x] = min(low[x], dfn[to]);
        }
    }
    if(low[x] == dfn[x])
    {
        int y;
        ++t;
        do
        {
            y = stk.top(); stk.pop();
            v[y] = 0;
            belong[y] = t;
            if(zi[y]) zih[t] = 1;
            //printf("belong[%d] == %d\n", y, belong[y]);
            q[t]++;
        }while(y != x);
    }
}

int main()
{
    freopen("maf.in", "r", stdin);
    freopen("maf.out", "w", stdout);

    n = read();
    for(int i=1; i<=n; i++)
    {
        int x = read();
        add(i, x);
        b[i].from = i; b[i].to = x;
        if(i == x) zi[i] = 1;
    }
    for(int i=1; i<=n; i++)
    {
        if(!dfn[i])
        {
            tarjan(i);
        }
    }
    for(int i=1; i<=n; i++)
    {
        if(belong[b[i].to] != belong[b[i].from])
        {
            du[belong[b[i].to]]++;
        }
    }
    //printf("t == %d\n", t);
    for(int i=1; i<=t; i++)
    {
        //printf("du[%d] == %d\n", i, du[i]);
        if(du[i] > 0)
        {
            ans1++; ans2++;
        }
    }
    for(int i=1; i<=t; i++)
    {
        if(q[i] == 1) continue;
        ans1 += (q[i]>>1);
        ans2 += q[i] - 1;
        //printf("ans1 == %d ans2 == %d\n", ans1, ans2);

        if(du[i] > 0)
        {
            ans1--;
        }
    }
    for(int i=1; i<=t; i++)
    {
        if(zih[i] && (!du[i]))
        {
            ans1++; ans2++;
        }
    }
    printf("%d %d", ans1, ans2);

    return 0;
}
我的第一版
/*
我知道我那个tarjan的为什么错了:
死的人最多能找对,但是最少的情况我把缩点之后入度不为零的都算进去了
尽管有关环的处理好像和正解一样

然后,开启借鉴模式
code is from:WintersRain

我似乎明白了
在万能头文件下kill是关键字
*/
#include <cstdio>
#include <cstring>
#include <string>

using namespace std;

typedef long long ll;
const int maxn = 1e6 + 2;
const int mod = 998244353;
const ll INF = 0x7fffffff;

int n, aim[maxn], lve, sze, pos, que[maxn], ipt[maxn];
bool kill[maxn], exist[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}
int main()
{
    freopen("maf.in", "r", stdin);
    freopen("maf.out", "w", stdout);

    n = read();
    for(int i=1; i<=n; i++)
    {
        aim[i] = read(); ipt[aim[i]]++;
    }
    for(int i=1; i<=n; i++)
    {
        if(ipt[i] == 0)
        {
            lve++;
            que[++sze] = i;
        }
    }
    pos = 1;
    while(pos <= sze)//que队列维护入度是0的点,入度是0一定能活,那么他的目标一定会死
    {
        int frnt = que[pos];
        pos++;
        if(kill[aim[frnt]]) continue;
        kill[aim[frnt]] = true;

        int targ = aim[aim[frnt]];
        ipt[targ]--;
        exist[targ] = true;//追求杀的人最多targ一定会死
        //in fact 追求杀的人最多的话有入度的都会死
        //如果杀的人最少,那还得看targ的入度是不是还有别的
        if(!ipt[targ]) que[++sze] = targ;
    }
    for(int i=1; i<=n; i++)
    {
        //为什么满足这个条件就一定在环里?
        //如果他不在环里,1是已经死了,如果还没死,被救一次就减一个入度
        //根据上面的循环,一个不在环里的点只有死掉和入度剩下0两种情况
        if(ipt[i] && !kill[i])
        {
            int ring = 0;//ring代表环的大小
            bool flag = false;
            for(int j=i; !kill[j]; j=aim[j])
            {
                ring++;
                if(exist[j]) flag = true;
                kill[j] = true;
            }
            if(!flag && ring > 1) lve++;//这是一个独立的环并且排除自杀的情况
            sze += ring/2;
        }
    }
    printf("%d %d", n-sze, n-lve);

    return 0;
}

D. 翻转游戏

题目描述

  • 翻转游戏是在一个  4*4的正方形上进行的,在正方形的 4 个格上每个格子都放着一个双面的物件。每个物件的两个面,一面是白色,另一面是黑色,每个物件要么白色朝上,要么黑色朝上,每一次你只能翻 1 个物件,从而由黑到白的改变这些物件上面的颜色,反之亦然。每一轮被选择翻转的物件遵循以下规则:

    1. 从 16个物件中任选一个。
    2. 翻转所选择的物件的同时,所有与它相邻的左方物件、右方物件、上方物件和下方物件(如果有的话),都要跟着翻转。
    • 以下为例:

      bwbw
      wwww
      bbwb
      bwwb
      
      • 这里  表示该格子放的物件黑色面朝上、 表示该格子放的物件白色朝上。如果我们选择翻转第三行的第一个物件,那么格子状态将变为:
      bwbw
      bwww
      wwwb
      wwwb
      
  • 游戏的目标是翻转到所有的物件白色朝上或黑色朝上。你的任务就是写一个程序来求最少的翻转次数来实现这一目标。

输入格式

输入文件包含  行,每行  个字符,每个字符  或  表示游戏开始时格子上物件的状态。

输出格式

输出文件仅一个整数,即从给定状态到实现这一任务的最少翻转次数。如果给定的状态就已经实现了目标就输出 0,如果不可能实现目标就输出: 。

样例

样例输入

bwwb
bbwb
bwwb
bwww

样例输出

4
//copy
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e6 + 2;
const int mod = 998244353;
const ll INF = 0x7fffffff;

int mp[6][6], n=4;
ll ans;
char s[5];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

void flip(int x, int y)
{
    mp[x-1][y] ^= 1;
    mp[x+1][y] ^= 1;
    mp[x][y-1] ^= 1;
    mp[x][y+1] ^= 1;
    mp[x][y] ^= 1;
}

void dfs(int lne, int stp, int opt)
{
    if(lne == n+1)
    {
        bool flag = true;
        for(int i=1; i<=n; i++)
        {
            if(mp[n][i] != opt) flag = false;
        }
        if(flag) ans = min(ans, (ll)stp);
        return;
    }
    int s = 0;
    for(int i=1; i<=n; i++)
    {
        if(mp[lne-1][i] != opt)
        {
            flip(lne, i);
            s |= (1<<(i-1));
            stp++;
        }
    }
    dfs(lne+1, stp, opt);
    for(int i=1; i<=n; i++)
    {
        if(s&(1<<(i-1))) flip(lne, i);
    }
}

int main()
{
    freopen("flip.in", "r", stdin);
    freopen("flip.out", "w", stdout);

    for(int i=1; i<=n; i++)
    {
        scanf("%s", s);
        for(int j=0; j<n; j++)
        {
            if(s[j] == 'b') mp[i][j+1] = 1;
        }
    }
    //把上面一行扔到下面一行处理
    //这样可以在不改变上一行其它棋子的情况下把上一行化成目标状态
    ans = INF;
    int Max = (1<<n)-1;
    for(int s=0; s<=Max; s++)//枚举第0行可能的状态,其实就是固定第一行的点击方式
    {
        for(int i=1; i<=n; i++)
        {
            if(s&(1<<(i-1))) mp[0][i] = 1;
            else mp[0][i] = 0;
        }
        dfs(1, 0, 0);
        dfs(1, 0, 1);
    }
    if(ans == INF) printf("Impossible");
    else printf("%lld", ans);

    return 0;
}

 

 
posted @ 2022-06-07 19:27  Catherine_leah  阅读(20)  评论(0编辑  收藏  举报
/* */