2019-08-15下午三道练习题CF1199
思路有点难想 but很好实现
这是原网站链接:传送门 这里只完成D, E, F三题
文章目录
- D:Welfare State
- 题目大意
- 正解
- 瞅瞅代码
- E:Matching vs Independent Set
- 题目大意
- 正解
- 代码实现
- F:Rectangle Painting 1
- 题目大意
- 正解
- 代码实现
D:Welfare State
题目大意
=
n个人
接下来一行有n个数,第i个数表示第i个人手上的钱a[i]
q次操作
操作分为两种:
1): 1 x 表示所有人手上的不能低于x ,即钱数少于x的人,把它的钱数变为x
2): 2 x y 表示第x个人的钱变为y
数据范围:
n (1≤n≤2⋅10^5) (0≤ai≤10^9) (1≤q≤2⋅10^5) (1≤p≤n, 0≤x≤10^9) (0≤x≤10^9)
正解
=
第二种操作很好解决,直接赋值覆盖即可
问题就在于第一种操作
如果我们读入一条语句就进行操作,那么就必须在线O(n)操作 坑定会炸!
想一下,在i个人的最后一次2操作j时,那么j+1~q次操作中的1操作都有可能影响i
所以我们就从后往前离线操作,记录到j次操作为止中1操作的最大值,与这个人最后一次2操作进行比较max
瞅瞅代码
#include <cstdio>
#include <iostream>
using namespace std;
#define MAXN 500005
struct node {int kind, x, y;
}a[MAXN];
int n, q, Max;
int res[MAXN];
bool flag[MAXN];
int main() {scanf ( "%d", &n );for ( int i = 1;i <= n;i ++ )scanf ( "%d", &res[i] );scanf ( "%d", &q );for ( int i = 1;i <= q;i ++ ) {a[i].y = -1;scanf ( "%d", &a[i].kind );if ( a[i].kind == 1 ) scanf ( "%d %d", &a[i].x, &a[i].y );else scanf ( "%d", &a[i].x );}for ( int i = q;i >= 1;i -- ) {if ( a[i].kind == 1 ) {if ( flag[a[i].x] ) continue;elseres[a[i].x] = max ( a[i].y, Max );flag[a[i].x] = 1;}elseMax = max ( Max, a[i].x );}for ( int i = 1;i <= n;i ++ ) {if ( ! flag[i] ) res[i] = max ( res[i], Max );printf ( "%d ", res[i] );}return 0;
}
E:Matching vs Independent Set
题目大意
T组数据,第一行输入n,m 共有3n个点,m条边
接下来m行,表示u,v两点之间有无向边相连
1)如果任选n条边,满足这n条边上的点只被一条边覆盖,即这些点的入度和出度和为1.eg:选两条边1,2和2,3就不符合题意因为2被两条边覆盖了
符合题意就输出Matching,第二行输出这n条边的编号
2)如果任选n条边后,满足至少n个点是孤立的,eg:选两条边1,2和2 3那么1,3就满足题意因为它们之间无直接边相连 (且1,3算两个点)
满足题意就输出IndSet,第二行输出这n个点的编号
3)如果都不满足就输出Impossible
(1≤n≤10^5, 0≤m≤5*10^5)1≤vi,ui≤3⋅n)
正解
首先要想明白,根本没有Impossible的可能,证明一下:因为共有3n个点,选n条边,最多也只能覆盖2n个点,那么剩下的n个点也是孤立的。
其次,这个如果是考试一般要绑点,就别想着骗分了
那么很好想不能满足边,就一定满足点
我们就在线操作,对u,v进行打标,满足边就tot++,最后判断tot输出就好了
还可用打标的flag数组进行答案输出
代码实现
#include <cstdio>
#include <cstring>
#define MAXN 300005
#define MAXM 500005
int n, m, t, cnt;
bool flag[MAXN];
int a[MAXM];
int main() {scanf ( "%d", &t );while ( t -- ) {for ( int i = 1;i <= 3 * n;i ++ )flag[i] = 0;cnt = 0;scanf ( "%d %d", &n, &m );for ( int i = 1;i <= m;i ++ ) {int u, v;scanf ( "%d %d", &u, &v );if ( flag[u] || flag[v] ) continue;else {flag[u] = flag[v] = 1;a[++ cnt] = i;}}if ( cnt >= n ) {printf ( "Matching\n" );for ( int i = 1;i <= n;i ++ )printf ( "%d ", a[i] );printf ( "\n" );}else {printf ( "IndSet\n" );int tot = 0;for ( int i = 1;i <= 3 * n;i ++ ) {if ( tot >= n ) break;if ( ! flag[i] ) {tot ++;printf ( "%d ", i );}}printf ( "\n" );}}return 0;
}
F:Rectangle Painting 1
题目大意
输入n,接下来输入nn的字符矩阵,‘#’, ‘.’
求最少把整个矩阵全都变为‘.’的代价和
自选矩阵大小hw以及矩阵的位置,然后被这个矩阵所包含的所有点都变为‘.’
代价为max (h, w)
n (1≤n≤50)
正解
首先拿到这个题,坑定暴搜是有分的,但会炸
那我们就思考假设对于一个23的矩阵,那么影响这个矩阵的方案有13+23(把第一行和第二行断开),12+22(把第一列和第二三列断开),22+12(把第一二列和第三列断开)然后在对于每一种断开继续以上操作,直到分出11的矩阵,就知道答案了
所以我们就要记忆化搜索,防止重复遍历超时,不断更新最小值
代码实现
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define MAXN 55
int n;
bool flag[MAXN][MAXN];
int dp[MAXN][MAXN][MAXN][MAXN];
int dfs ( int sx, int sy, int ex, int ey ) {if ( dp[sx][sy][ex][ey] != -1 ) return dp[sx][sy][ex][ey];if ( sx == ex && sy == ey ) return dp[sx][sy][ex][ey] = flag[sx][sy];int result1 = 0x7f7f7f7f, result2 = 0x7f7f7f7f;if ( sx != ex )for ( int i = sx;i < ex;i ++ )result1 = min ( result1, dfs ( sx, sy, i, ey ) + dfs ( i + 1, sy, ex, ey ) );if ( sy != ey )for ( int i = sy;i < ey;i ++ )result2 = min ( result2, dfs ( sx, sy, ex, i ) + dfs ( sx, i + 1, ex, ey ) );return dp[sx][sy][ex][ey] = min ( min ( result1, result2 ), max ( ( ex - sx + 1 ), ( ey - sy + 1 ) ) );
}
int main() {scanf ( "%d", &n );for ( int i = 1;i <= n;i ++ ) {scanf ( "\n" );for ( int j = 1;j <= n;j ++ ) {char tu;scanf ( "%c", &tu );if ( tu == '#' ) flag[i][j] = 1;else flag[i][j] = 0;}}memset ( dp, -1, sizeof ( dp ) );int result = dfs ( 1, 1, n, n );printf ( "%d", result );return 0;
}
连A三道,偶的妈呀!大神这是要逆天啦!!!
你这次考了倒数第五名,从倒数第一进步了五名,这么学下去,你不得牛死!
好了代码,或者思路有任何不懂的,在评论区留言,我看见会给你进行答复知道你懂为止 ,让我们下期再见,bye~~