2019-08-21 纪中NOIP模拟A组

T1 [JZOJ6315] 数字

题目描述

数据范围

  

分析

  我叕正解写挂了,写模拟一定要仔细考虑细节啊

  正解是这样说的

  我是在 $n$ 串中按位数从小到大枚举数字匹配,思路和正解相似,时间复杂度相同,做法稍有不同

#include <iostream>
#include <cstdio>
#include <cstdlib> 
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define ll long long

ll inf = 1e18, p[20] = {1};
int t, n, a[20];
char s[20];

ll solve() {
    for (int i = 1; i <= n; i++) {
        ll ans = inf;
        for (int j = 1; j <= i; j++) {
            if (!a[j]) continue;
            ll now = 0; int len = i, k = j + i, f = 0;
            for (int l = 0; l < i; l++) {
                if (j + l <= n) now = now * 10 + a[j + l];
                else now = now * 10 + a[j + l - i];
            }
            if (j + i - 1 > n) {
                ll last = now % p[j + i - 1 - n];
                now = now - last + (last + 1) % p[j + i - 1 - n];
            }
            for (int l = 1; j - l; l++)
                if (a[j - l] != (now - 1) / p[l - 1] % 10) {f = 1; break;}
            if (f) continue;
            if (++now / p[len]) len++;
            while (k + len - 1 <= n) {
                for (int l = 0; l < len; l++)
                    if (a[k + l] != now / p[len - l - 1] % 10) {f = 1; break;}
                if (f) break; k += len;
                if (++now / p[len]) len++;
            }
            if (f) continue;
            if (k > n) now--;
            else {
                for (int l = 0; k + l <= n; l++)
                    if (a[k + l] != now / p[len - l - 1] % 10) {f = 1; break;}
                if (f) continue;
            }
            ans = min(ans, now);
        }
        if (ans < inf) return ans;
    }
}

int main() {
    scanf("%d", &t);
    for (int i = 1; i <= 18; i++) p[i] = p[i - 1] * 10;
    while (t--) {
        scanf("%s", s + 1); n = strlen(s + 1);
        for (int i = 1; i <= n; i++) a[i] = s[i] - '0';
        printf("%lld\n", solve());
    } 
    
    return 0;
}
View Code

T2 [JZOJ6313] Maja

题目描述

  蜜蜂 $Maja$ 到了一片草地,草地可以被描述成 $N$ 行 $M$ 列的网格图,在第 $i$ 行第 $j$ 列的位置上有 $C_{i,j}$ 朵未授粉的花。

  $Maja$ 会从第 $A$ 行第 $B$ 列出发,每次只能移动到与当前位置四相邻的格子上,且不能移动到草地以外。每到达一个格子,她会把此处所有未授粉的花都授粉。

  然而,当 $Maja$ 离开一个格子,此处又会长出 $C_{i,j}$ 朵未授粉的花。

  $Maja$ 想知道,如果她从第 $A$ 行第 $B$ 列出发,选择一条长度恰好为 $K$ 的路径,最后又回到第 $A$ 行第 $B$ 列,最多能为多少朵花授粉。

数据范围

  对于 $40\%$ 的数据,$K \leq 10^4$

  对于 $100\%$ 的数据,$2 \leq N,M \leq 100$,$2 \leq K \leq 10^9$

分析

  很容易发现,最优路径是先走到某个点处开始绕环走,最后再原路回到起点

  又很容易发现,最优情况下环上只有两个点

  设 $f[i][j][k]$ 表示经过 $k$ 步走到点 $(i,j)$ 处时的最大点权和

  对于每个 $f$ 值,可以在点 $(i,j)$ 与其四相邻中点权最大的点上来回走,求出该情况下的最优值

  观察可知 $k$ 值最大为 $min(n \times m, \frac{k}{2})$,但空间复杂度仍无法承受,因此需要滚动数组

#include <iostream>
#include <cstdio>
#include <cstdlib> 
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 101

int n, m, a, b, t;
ll ans, g[N][N], h[N][N], f[N][N][2];
int d[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};

bool check(int x, int y) {
    if (x < 1 || x > n) return false;
    if (y < 1 || y > m) return false;
    return true;
}

int main() {
    scanf("%d%d%d%d%d", &n, &m, &a, &b, &t);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            scanf("%lld", &g[i][j]);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            for (int k = 0; k < 4; k++) {
                int x = i + d[k][0], y = j + d[k][1];
                if (!check(x, y)) continue;
                h[i][j] = max(h[i][j], g[i][j] + g[x][y]);
            }
    int limit = min(n * m, t / 2);
    memset(f, -1, sizeof f); f[a][b][0] = 0;
    for (int k = 0; k <= limit; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++) {
                for (int l = 0; l < 4; l++) {
                    int x = i + d[l][0], y = j + d[l][1];
                    if (!check(x, y) || f[x][y][(k & 1) ^ 1] == -1) continue;
                    f[i][j][k & 1] = max(f[i][j][k & 1], f[x][y][(k & 1) ^ 1] + g[i][j]);
                }
                if (f[i][j][k & 1] == -1) continue;
                ans = max(ans, f[i][j][k & 1] * 2 + (t / 2 - k) * h[i][j] - g[i][j]);
            }
    printf("%lld", ans);
    
    return 0;
}
View Code

T3 [JZOJ6316] djq的朋友圈

题目描述

数据范围

分析

  前面的 $70 \, pts$ 用状压 $dp$ 是能够想到的,先状压处理出通过 $1$ 的每个熟人,使得 $1$ 的所有非熟人是否能确立与 $1$ 关系,是否与 $1$ 为盟友,然后对已排列的熟人的集合的状态进行转移

  我们观察发现,当 $1$ 的熟人个数大于 $20$ 时,$1$ 的非熟人个数一定小于等于 $20$,所以只需要把状态改为已确立关系的非熟人的集合,用相似的方法转移

#include <iostream>
#include <cstdio>
#include <cstdlib> 
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 44
#define M 1 << (N >> 1)

int n, m, cnt1, cnt2, ans;
int g[N][N], f[M], num[N];
ll vis[N], rel[N];

int Cnt(ll s) {
    int sum = 0;
    while (s) s &= (s - 1), sum++;
    return sum;
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        if (w) g[u][v] = g[v][u] = -1;
        else g[u][v] = g[v][u] = 1;
    }
    for (int i = 2; i <= n; i++)
        if (g[1][i]) {
            num[i] = ++cnt1;
            if (g[1][i] > 0) ans++;
            for (int j = 2; j <= n; j++)
                if (!g[1][j] && g[i][j]) {
                    if (!num[j]) num[j] = ++cnt2;
                    vis[num[i]] |= (1ll << num[j] - 1);
                    if (g[1][i] * g[i][j] > 0)
                        rel[num[i]] |= (1ll << num[j] - 1);
                }
        }
    memset(f, -1, sizeof f); f[0] = 0;
    if (cnt1 <= 20) {
        for (int i = 0; i < (1 << cnt1); i++)
            if (f[i] >= 0) {
                ll now = 0;
                for (int j = 1; j <= cnt1; j++)
                    if (i & (1 << j - 1)) now |= vis[j];
                for (int j = 1, k; j <= cnt1; j++)
                    if ((k = i | (1 << j - 1)) != i)
                        f[k] = max(f[k], f[i] + Cnt(rel[j] & (~now)));
            }
        ans += f[(1 << cnt1) - 1];
    }
    else {
        for (int i = 0; i < (1 << cnt2); i++)
            if (f[i] >= 0)
                for (int j = 1, k; j <= cnt1; j++)
                    if ((k = i | vis[j]) != i)
                        f[k] = max(f[k], f[i] + Cnt(rel[j] & (~i)));
        ans += f[(1 << cnt2) - 1];
    }
    printf("%d", ans);
    
    return 0;
}
View Code
posted @ 2019-08-21 15:40  Pedesis  阅读(172)  评论(0编辑  收藏  举报