CSP-J2020題解


A 優秀的拆分

顯然每個數只有一種分法。從大到小枚舉\(2\)的正整數次冪,能拆則拆。

若未拆完,則是無解情況。

#include <bits/stdc++.h>

using namespace std;

int ans[50], l;

int main()
{
	freopen("power.in", "r", stdin);
	freopen("power.out", "w", stdout);
	int n;
	scanf("%d", &n);
	for (int i = 24; i && n; i--)
		if (n >= (1 << i))
			n -= (1 << i), ans[++l] = (1 << i);
	if (n)
	{
		puts("-1"); return 0;
	}
	for (int i = 1; i <= l; i++)
		printf("%d ", ans[i]);
	return 0;
}

B 直播獲獎

觀察到\(a_i\)不超過\(600\),可以直接開桶記錄每個分數有多少人。

枚舉分數線。若當前人數大於等於獲獎人數,該分數線即為答案。

#include <bits/stdc++.h>

using namespace std;
const int N = 1e5, M = 600;

int n, w;
int a[M + 5];

int main()
{
	freopen("live.in", "r", stdin);
	freopen("live.out", "w", stdout);
	scanf("%d%d", &n, &w);
	for (int i = 1; i <= n; i++)
	{
		int x, s = 0, t = max(1, i * w / 100);
		scanf("%d", &x), a[x]++;
		for (int j = M; j >= 0; j--)
		{
			s += a[j];
			if (s >= t) { printf("%d ", j); break; } 
		}
	}
	return 0;
}

C 表達式

首先建出表達式樹。顯然變量為葉子節點,符號為非葉子節點。記\(f_i\)\(i\)節點改變是否有影響,轉移按符號分類討論即可。

#include <bits/stdc++.h>

using namespace std;
const int N = 1e6 + 5;

int e[N][2], vl[N], id[N], a[N], st[N], f[N];
int n, top, cnt;
char s[N];

void dfs(int x)
{
    int y0 = e[x][0], y1 = e[x][1];
    if (f[x] == -1)
    {
        if (vl[y0] && vl[y1]) dfs(y0), dfs(y1);
        if (!vl[y0] && vl[y1]) dfs(y0);
        if (vl[y0] && !vl[y1]) dfs(y1);
    }
    else if (f[x] == -2)
    {
        if (!vl[y0] && !vl[y1]) dfs(y0), dfs(y1);
        if (vl[y0] && !vl[y1]) dfs(y0);
        if (!vl[y0] && vl[y1]) dfs(y1);
    }
    else if (f[x] == -3) dfs(y0);
    else f[x] = 1;
}

int main()
{
    int q, x, y, l;
    gets(s), l = strlen(s);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 0; i < l; i++)
        if (s[i] == 'x')
        {
            i++;
            for (x = 0; s[i] != ' ' && i < l; i++) x = x * 10 + s[i] - '0';
            vl[id[x] = st[++top] = ++cnt] = a[x];
        }
        else if (s[i] == '&')
            cnt++, vl[cnt] = vl[e[cnt][0] = st[top--]] & vl[e[cnt][1] = st[top--]], f[st[++top] = cnt] = -1;
        else if (s[i] == '|')
            cnt++, vl[cnt] = vl[e[cnt][0] = st[top--]] | vl[e[cnt][1] = st[top--]], f[st[++top] = cnt] = -2;
        else if (s[i] == '!')
            cnt++, vl[cnt] = !vl[e[cnt][0] = st[top--]], f[st[++top] = cnt] = -3;
    dfs(cnt);
    for (scanf("%d", &q); q--;)
        scanf("%d", &x), printf("%d\n", vl[cnt] ^ f[id[x]]);
    return 0;
}

D 方格取數

\(f_{i,j}\)為小能走到\((i,j)\)這個點答案最大能多少。

注意到小能只能在橫方向只能向右走,可以先枚舉j。

轉移時先將初值從左邊轉移。

再從上往下、從下往上更新\(f\)。(其中一次需要開臨時數組)

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 1005;

int a[N][N];
ll f[N][N], g[N][N];
int n, m;

int main()
{
	freopen("number.in", "r", stdin);
	freopen("number.out", "w", stdout);
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			scanf("%d", &a[i][j]);
	for (int i = 0; i <= n + 1; i++)
		for (int j = 0; j <= m + 1; j++)
			g[i][j] = f[i][j] = -1145141919810;
	f[1][1] = a[1][1];
	for (int j = 1; j <= m; j++)
	{
		for (int i = 1; i <= n; i++) g[i][j] = f[i][j] = max(f[i][j], f[i][j - 1] + a[i][j]);
		for (int i = 1; i <= n; i++) f[i][j] = max(f[i][j], f[i - 1][j] + a[i][j]);
		for (int i = n; i; i--) g[i][j] = max(g[i][j], g[i + 1][j] + a[i][j]);
		for (int i = 1; i <= n; i++) f[i][j] = max(f[i][j], g[i][j]);
	}
	printf("%lld\n", f[n][m]);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM