2024.11.20
今日总结:
上午贪心专题,下午Dp和搜索专练,晚上听了构造和继续专练
1:Advertisement 2
这道题的贪心思路是将两点的二维差值,转换为单点自差,便利每一个点判断是否满足条件即可复杂度O(n)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
const int INF = 0x3f3f3f3f;
int n,ans,Max = -INF;
bool st[N];
struct Node
{
int x,y;
bool operator < (const Node &a) const{ if(y == a.y) return x > a.x; return y > a.y;}
}q[N];
int main()
{
// freopen("advertisement.in","r",stdin);
// freopen("advertisement.out","w",stdout);
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
{
int x,e;
// scanf("%d%d",&q[i].x,&q[i].e);
scanf("%d%d",&x,&e);
q[i] = {e - x,e + x};
}
sort(q + 1,q + n + 1);
for(int i = 1;i <= n;i ++)
{
if(q[i].x > Max)
{
Max = q[i].x;
ans ++;
}
/*
if(!st[i])
{
st[i] = true;
for(int j = i + 1;j <= n;j ++)
{
if(abs(q[i].x - q[j].x) <= abs(q[i].e - q[j].e)) st[j] = true;
}
ans ++;
}
*/
}
printf("%d\n",ans);
return 0;
}
2:[牛奶规划](http://www.nfls.com.cn:20035/contest/2082/problem/2
这道题的贪心思路是用优先队列从后往前便利,主要是利用了优先队列的队头永远都是最大值,复杂度是O(n^2)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int n;
int d[N],g[N];
vector<int> vec[N];
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
{
scanf("%d%d",&g[i],&d[i]);
vec[d[i]].push_back(g[i]);
}
int ans = 0;
priority_queue<int> pq;
for(int i = N;i >= 1;i --)
{
for(auto x : vec[i])
{
pq.push(x);
cout << '*' << x << " ";
}
if(!pq.empty())
{
ans += pq.top();
// cout << pq.top() << endl;
pq.pop();
}
}
printf("%d\n",ans);
return 0;
}
3:集市班车
这道题的贪心思路是利用大根堆的限制,利用区间差
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4 + 10;
int k,n,c,ans;
int res[N];
struct Node
{
int m,s,t;
}a[N];
bool cmp(Node &a,Node &b)
{
return a.t < b.t;
}
int main()
{
scanf("%d%d%d",&k,&n,&c);
for(int i = 1;i <= k;i ++)
scanf("%d%d%d",&a[i].s,&a[i].t,&a[i].m);
sort(a + 1,a + k + 1,cmp);
for(int i = 1;i <= k;i ++)
{
int p = 1;
while(a[i].s < res[p] && p <= c)
p ++;
if(p == c + 1) continue;
for(int j = p;j - p + 1 <= a[i].m && j <= c;j ++)
res[j] = a[i].t;
ans += min(c - p + 1,a[i].m);
sort(res + 1,res + c + 1,greater<int>());
}
printf("%d\n",ans);
return 0;
}
4:词典
去年NOIP第一题,主要贪心思路就是对于每个给出的字符串,求出他的最大字典序和最小字典序,让当前要求的最小的字符串用最小的字典序和出自己以外所有的字符串的最大的字典序做比较即可
复杂度O(n^2)或者是O(nm)因为n,m范围一样
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 3010;
int n,m;
char s[N];
int ans_min[N],ans_max[N];
int main()
{
// freopen("dict.in","r",stdin);
// freopen("dict.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i ++)
{
scanf("%s",s);
for(int j = 0;j < m;j ++)
{
if(j == 0) ans_min[i] = ans_max[i] = int(s[j] - 'a');
else
{
ans_min[i] = min(ans_min[i],int(s[j] - 'a'));
ans_max[i] = max(ans_max[i],int(s[j] - 'a'));
}
}
}
for(int i = 1;i <= n;i ++)
{
bool flag = false;
for(int j = 1;j <= n;j ++)
{
if(i == j) continue;
if(ans_min[i] < ans_max[j]) continue;
else
{
flag = true;
printf("0");
break;
}
}
if(!flag) printf("1");
}
return 0;
}
5:染色
这道题是今年csp-s的原题,考场上想的太复杂了以为是一道紫题其实只是一道很有思维的纯线性Dp的蓝题
对于这道题考虑dp但不能拿满分,还需考虑前缀和优化,主要是因为有求和的过程,这样可以减少时间复杂度,只需让dp[i]表示统计到第i个数的总和的最大值即可,用一下延迟数组,记录上一个与自己颜色相同的点的最近的位置,point为点的位置,转移方程为dp[i] = max(dp[i],dp[1~point] + a[point] = [point~i]的和)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e6 + 10;
ll n;
ll a[N],dp[N],s[N],app[N];
int main()
{
ll T;
scanf("%lld",&T);
while(T --)
{
memset(dp,0,sizeof(dp));
memset(app,0,sizeof(app));
memset(s,0,sizeof(s));
scanf("%lld",&n);
for(ll i = 1;i <= n;i ++)
scanf("%lld",&a[i]);
for(ll i = 2;i <= n;i ++)
{
if(a[i] == a[i - 1]) s[i] = s[i - 1] + a[i];
else s[i] = s[i - 1];
}
for(ll i = 1;i <= n;i ++)
{
dp[i] = dp[i - 1];
if(app[a[i]]) dp[i] = max(dp[i],dp[app[a[i]] + 1] + a[i] + s[i] - s[app[a[i]] + 1]);
app[a[i]] = i;
}
printf("%lld\n",dp[n]);
}
return 0;
}
6:引水入城
这道题是一道搜索 + Dp的题目,主要是因为走的方向和位置并不固定,需要对左边界和右边界求最小值和最大值,然后搜索
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 550;
int n,m,ans;
int h[N][N],l[N][N],r[N][N];
bool vis[N][N];
int dx[5] = {-1,0,1,0}; // 上左下右
int dy[5] = {0,1,0,-1};
void dfs(int x,int y)
{
vis[x][y] = true;
for(int i = 0;i < 4;i ++)
{
int xx = x + dx[i],yy = y + dy[i];
if(xx < 1 || xx > n || yy < 1 || yy > m) continue; // 跳出循环
if(h[xx][yy] >= h[x][y]) continue;
if(!vis[xx][yy]) dfs(xx,yy);
l[x][y] = min(l[x][y],l[xx][yy]); // 更新左端点最小值
r[x][y] = max(r[x][y],r[xx][yy]); // 更新右端点最大值
// 找出最大的输水范围
}
}
int main()
{
memset(l,0x3f,sizeof(l));
memset(r,0,sizeof(r));
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= m;j ++)
scanf("%d",&h[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); // 往下传
}
bool flag = false;
for(int i = 1;i <= m;i ++) // 遍历干旱区未被安装设备的位置
{
if(!vis[n][i])
{
flag = true;
ans ++;
}
}
if(flag)
{
printf("0\n");
printf("%d\n",ans);
return 0;
}
int ll = 1,rr = m;
while(ll <= rr)
{
int Max = 0;
for(int i = 1;i <= m;i ++)
{
if(l[1][i] <= ll) Max = max(Max,r[1][i]);
}
ans ++;
ll = Max + 1;
}
printf("1\n");
printf("%d\n",ans);
return 0;
}
7:传染病控制
这道题是一道在树上的dfs他需要dfs两遍,需要处理的是断边后的变化
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 410;
const int M = N * 2;
int n,m,ans,Max = -1;
int dep[N],fa[N],siz[N],ct[N];
int head[N],to[M],nxt[M],idx;
vector<int> k[N];
void Add(int a,int b)
{
idx ++;
to[idx] = b;
nxt[idx] = head[a];
head[a] = idx;
}
void dfs_1(int u,int f,int depth)
{
fa[u] = f,siz[u] = 1;dep[u] = depth;
Max = max(Max,depth);
for(int i = head[u];i;i = nxt[i])
{
int j = to[i];
if(j != f)
{
dfs_1(j,u,depth + 1);
siz[u] += siz[j];
}
}
}
void dfs_2(int depth,int num)
{
if(depth == Max + 1)
{
ans = min(ans,num);
return;
}
for(auto i : k[depth]) // 切断后续连接
{
if(ct[fa[i]]) ct[i] = 1;
else ct[i] = 0;
}
bool flag = 1;
for(auto i : k[depth])
if(!ct[i]) flag = false;
if(flag)
{
ans = min(ans,num);
return;
}
for(auto i : k[depth])
{
if(ct[i]) continue;
ct[i] = 1;
dfs_2(depth + 1,num - siz[i]);
ct[i] = 0;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i ++)
{
int x,y;
scanf("%d%d",&x,&y);
Add(x,y),Add(y,x);
}
dfs_1(1,0,1);
for(int i = 1;i <= n;i ++)
k[dep[i]].push_back(i);
ans = n;
dfs_2(2,n);
printf("%d\n",ans);
return 0;
}