首页 > 其他分享 >NOIP 2010 题解

NOIP 2010 题解

时间:2023-04-19 17:13:44浏览次数:48  
标签:卡片 NOIP int 题解 fx fy 505 2010 dp

机器翻译

单向链表,如果 \(i\) 在内存里,那么用 \(nxt[i]\) 来记录他的下一个单词,每次要插入的时候,如果当前链表的长度小于 \(m\),那么直接把他插入的末尾,如果等于 \(m\),就把链表的第一个从链表里弹出来,再把这个元素加进去。

\(Code :\)

#include <bits/stdc++.h>
using namespace std;
int n, m, last = -1, now = -1, siz, nxt[1005], ans;
bool in[1005];
int main()
{
	scanf("%d%d", &m, &n);
	for (int i = 1; i <= n; i ++)
	{
		int x;
		scanf("%d", &x);
		if (!in[x])
		{
			if (siz < m)
			{
				if (now == -1) last = x;
				in[x] = true;
				siz ++;
				if (now != -1) nxt[now] = x;
				now = x;
			}
			else
			{
				in[last] = false;
				last = nxt[last];
				in[x] = true;
				nxt[now] = x;
				now = x;
			}
			ans ++;
		}
	}
	printf("%d", ans);
	return 0;
}

乌龟棋

发现每张卡片的数量很少,于是设 \(dp[a][b][c][d]\) 表示用 \(a\) 张 \(1\),\(b\) 张 \(2\),\(c\) 张 \(3\),\(d\) 张 \(4\),所能获得的分数的最大值。

转移时考虑一下四种情况:

  1. 现在这一步使用的是卡片1,则 \(dp[a][b][c][d] = max(dp[a][b][c][d], dp[a - 1][b][c][d] + a[x])\)。
  2. 现在这一步使用的是卡片2,则 \(dp[a][b][c][d] = max(dp[a][b][c][d], dp[a][b - 1][c][d] + a[x])\)。
  3. 现在这一步使用的是卡片3,则 \(dp[a][b][c][d] = max(dp[a][b][c][d], dp[a][b][c - 1][d] + a[x])\)。
  4. 现在这一步使用的是卡片4,则 \(dp[a][b][c][d] = max(dp[a][b][c][d], dp[a][b][c][d - 1] + a[x])\)。

其中 \(x\) 表示现在到达的格子的编号,即 \(a + 2b + 3c + 4d + 1\)。
枚举 \(a,b,c,d\),最后答案为 \(dp[\)卡片\(1\)的个数\(][\)卡片\(2\)的个数\(][\)卡片\(3\)的个数\(][\)卡片\(4\)的个数\(]\)。

\(Code :\)

#include <bits/stdc++.h>
using namespace std;
int dp[41][41][41][41], n, m, a[351], cnt[5];
int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	for (int i = 1; i <= m; i ++)
	{
		int x;
		scanf("%d", &x);
		cnt[x] ++;
	}
	dp[0][0][0][0] = a[1];
	for (int i = 0; i <= cnt[1]; i ++) for (int j = 0; j <= cnt[2]; j ++) for (int u = 0; u <= cnt[3]; u ++) for (int v = 0; v <= cnt[4]; v ++)
				{
					int x = i + j * 2 + u * 3 + v * 4 + 1;
					if (i) dp[i][j][u][v] = max(dp[i][j][u][v], dp[i - 1][j][u][v] + a[x]);
					if (j) dp[i][j][u][v] = max(dp[i][j][u][v], dp[i][j - 1][u][v] + a[x]);
					if (u) dp[i][j][u][v] = max(dp[i][j][u][v], dp[i][j][u - 1][v] + a[x]);
					if (v) dp[i][j][u][v] = max(dp[i][j][u][v], dp[i][j][u][v - 1] + a[x]);
				}
	printf("%d", dp[cnt[1]][cnt[2]][cnt[3]][cnt[4]]);
}

关押罪犯

并查集。

由于只需要知道最大仇恨值的最小值,所以我们先将仇恨按仇恨值从大到小排序。

按照顺序处理每一条边,设当前处理到的两个罪犯是 \(a\) 和 \(b\)。

如果当前的他们不在同一个并查集当中,那么按照敌人的敌人是朋友的原则,我们把 \(a\) 与和 \(b\) 仇恨值最大的哪个罪犯合并在一起,这样就不会让 \(b\) 和和 \(b\) 仇恨值最大的罪犯打起来,同理,将 \(b\) 与和 \(a\) 矛盾值最大的罪犯和并起来。

如果当前两个罪犯已经在一个并查集当中了,那么无论如何也避免不了他们之间的仇恨了,之间输出矛盾值即可。

\(Code :\)

#include <bits/stdc++.h>
using namespace std;
int n, m, fa[20001], x, y, maxn[20001];
struct node
{
    int a, b, c;
} e[100001];
bool cmp(node a, node b)
{
    if (a.c > b.c) return true;
    else return 0;
}
int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]);}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i ++) scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].c);
    for (int i = 1; i <= n; i ++) fa[i] = i;
    sort(e + 1, e + m + 1, cmp);
    for (int i = 1; i <= m; i ++)
    {
        x = find(e[i].a);
        y = find(e[i].b);
        int a = e[i].a, b = e[i].b;
        if (x == y)
        {
            printf("%d", e[i].c);
            return 0;
        }
        if (!maxn[a]) maxn[a] = b;
        else fa[find(maxn[a])] = y;
        if (!maxn[b]) maxn[b] = a;
        else fa[find(maxn[b])] = x;
    }
    printf("0");
    return 0;
}

引水入城

猜想:对于任何点,水往下流,其覆盖的必然是一个连续的区间。

证明:反证法。

前提:所有干旱区都能被覆盖到。

假设有一条水流如下图。
image

假设这条水流只能覆盖到第 \(n\) 行的第三个和第六个格子,那么如果有其它的格子能够到达第四个和第五个格子,则必定要经过黑色的线,则经过的那个点可以到达中间的第四个格子和第五个格子,与假设矛盾。

image

如图,无论是上图那一条红线,都必定经过黑线。

知道了这个,下面我们就只要在dfs的时候把区间的左右端点求出来,再做线段覆盖就行了。

#include <bits/stdc++.h>
using namespace std;
int n, m, a[505][505], l[505][505], r[505][505], dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1}, cnt;
bool vis[505][505];
void dfs(int x, int y)
{
	vis[x][y] = true;
	for (int i = 0; i <= 3; i ++)
	{
		int fx = x + dx[i], fy = y + dy[i];
		if (fx && fy && fx <= n && fy <= m && a[x][y] > a[fx][fy])
		{
			if (!vis[fx][fy]) dfs(fx, fy);
			l[x][y] = min(l[x][y], l[fx][fy]);
			r[x][y] = max(r[x][y], r[fx][fy]);
		}
	}
}
int main()
{
	scanf("%d%d", &n, &m);
	memset(l, 0x7f, sizeof(l));
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= m; j ++) scanf("%d", &a[i][j]);
	for (int i = 1; i <= m; i ++)
		l[n][i] = r[n][i] = i;
	for (int i = 1; i <= m; i ++)
		if (!vis[1][i]) dfs(1, i);
	for (int i = 1; i <= m; i ++)
		if (!vis[n][i]) cnt ++;
	if (cnt)
	{
		printf("0\n%d", cnt);
		return 0;
	}
	int left = 1;
	while (left <= m)
	{
		int maxr = -1e9;
		for (int i = 1; i <= m; i ++)
			if (l[1][i] <= left) maxr = max(maxr, r[1][i]);
		left = maxr + 1;
		cnt ++;
	}
	printf("1\n%d", cnt);
	return 0;
}

标签:卡片,NOIP,int,题解,fx,fy,505,2010,dp
From: https://www.cnblogs.com/tongyuxin/p/17333925.html

相关文章

  • 题解 P9130 【[USACO23FEB] Hungry Cow P】
    赛时开始一眼线段树分治,交了几发都T了,就意识到事情不对。后来想了想发现势能分析不能带撤销。。。后来加了一些不能改变复杂度假了的优化,没过之后就自闭跑路了。。。赛后听别人说了个楼房重建就明白怎么做了。首先,我们离线下来把\(a\)排序,去重(这样方便一点,不然权值线段树上......
  • api-ms-win-core-file-l1-2-0.dll文件问题解决
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或者损坏了,这时你只需下载这个api-ms-win-core-file-l1-2-0.dll文件进行安装(前提是找到适合的版本),当我们执行某......
  • P2680 NOIP2015 提高组 运输计划
    P2680NOIP2015提高组运输计划最小化最长的路径,考虑二分答案。问题转化成检验删去一条边的边权后,最长路径权值能否不超过\(x\)。考虑没删边权时,原先那些不超过\(x\)的路径,删去边权后肯定不会影响,直接忽略。考虑原先比\(x\)长的那些路径。我们期望删边权后这些路径全部......
  • 2023联合省选题解
    2023联合省选题解火车站签到题。可以发现,一段被覆盖的区间上任意两点联通,因此用差分维护连续段即可。intmain(){n=read(),m=read(),x=read();for(inti=1;i<=m;i++){intl=read(),r=read();bl[l]=1;br[r]=1;c[l]++,c[r+1]--;......
  • Pwn系列之Protostar靶场 Stack6题解
    源码如下:#include<stdlib.h>#include<unistd.h>#include<stdio.h>#include<string.h>voidgetpath(){charbuffer[64];unsignedintret;printf("inputpathplease:");fflush(stdout);gets(buffer);ret=__b......
  • VS2010在使用过程中遇到的问题
    一、解决执行后看不到结果,只是屏幕一闪。第一次使用vs2010的同学可能会遇到在执行文件(执行文件·:按下CTRL+F5)时,只出现屏幕一闪,没有看到结果。那么不用慌,这不代表你没有成功。只是,执行速度快,一闪而过。解决方法,如下步骤:1)右击该项目 2)点击属性3)点击连接器4)点击系统5)在右侧会看到子系......
  • CF题解
    E.ReplacetheNumbers1900思维https://codeforces.com/problemset/problem/1620/E题解:正着做比较困难,我们可以考虑从后往前做。一个数会被变成什么样子是取决于其后的2操作。2操作可以等价为一个变换,而位置越后的2操作相较前面的2操作起着决定性作用,故从后往前遍历时前面的......
  • 【题解】P6292 区间本质不同子串个数
    原题链接区间本质不同子串个数题目描述给定一个长度为\(n\)的字符串\(S\),\(m\)次询问由\(S\)的第\(L\)到第\(R\)个字符组成的字符串包含多少个本质不同的子串。定义两个字符串\(a,b\)相同当且仅当\(|a|=|b|\)并且对于\(i\in[1,|a|]\)都有\(a_i=b_i\)。输入......
  • 超级码力初赛第二场 五字回文 题解
    题目描述小栖最近很喜欢回文串,由于小栖的幸运数字是5,他想知道形似“abcba"的回文串在他给定的字符串中的数量s.length<=10^6字符串s只包含小写字母示例示例1:输入:s="abcba"输出:1示例2:输入:s="abcbabcccb"输出:2解释:形似”abcba“的字符串有”abcba“和”cbab......
  • CF题解
    D.GuessthePermutation2000逆序性质二分https://codeforces.com/contest/1589/problem/D题解:首先我们可以二分查找i的位置:当1->x逆序对>0,则在i右,否则在左,log(n)次询问。找到i的位置后,我们发现逆序对有如下性质,i->j-1的数量和i+1->j-1的数量差为j-1-i,故我们可以询问i+1->n......