I. Running S
洛谷 P1353 [USACO08JAN] Running S
题意
给定2个整数
n
n
n和
m
m
m分别代表运动的总时间和奶牛所能承受的疲劳度,接下来是
n
n
n个数字,均代表奶牛在第下标分钟内所能移动的距离,奶牛在每分钟内有两种选择:1、运动,但会增加单位一的疲劳度 2、休息,会减少单位一的疲劳度,但是一旦开始休息,就必须休息至疲劳度减为0。
现在要使这个奶牛在疲劳度在整个过程中不会超过其所能承受范围(
m
m
m)的情况之下移动的距离最远,求解这个最远的距离。
思路
这道题十分显然是一个动态规划,
d
p
dp
dp 数组会受到两个重要的因素的影响:当前的时间和当前的疲劳度,所以说
d
p
dp
dp 数组应该是一个二维数组,两个因素分别占一个下标,
d
p
i
,
j
dp_{i,j}
dpi,j 内存储的是在第
i
i
i 分钟时疲劳度为
j
j
j 的最远距离。
由第一种情况是运动,由于每次运动增加一单位的疲劳度,并且一次运动是在一分钟内的,所以可以很容易地得出
d
p
i
,
j
=
m
a
x
(
d
p
i
,
j
,
d
p
i
−
1
,
j
−
1
+
a
i
)
dp_{i,j} = ~max(~dp_{i,j},~dp_{i-1,j-1}+a_i~)
dpi,j= max( dpi,j, dpi−1,j−1+ai )
第二种情况是休息,由于休息后必须将疲劳度降回0,我们遍历求值中数组的
j
j
j 就应该是0,那么当前的时间与我们需要查询的上一个位置的时间之差就应该是上一个位置的疲劳度,即
d
p
i
,
j
=
m
a
x
(
d
p
i
,
j
,
d
p
i
−
k
,
k
)
(
j
=
0
,
k
≤
m
i
n
(
i
,
m
)
)
dp_{i,j}=max(~dp_{i,j},~dp_{i-k,k}~)~(j=0,k\leq min(i,m))
dpi,j=max( dpi,j, dpi−k,k ) (j=0,k≤min(i,m))
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,a[10005],dp[10005][505];
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
dp[i][0]=max(dp[i][0],dp[i-1][0]);
for(int j=1;j<=min(i,m);j++){
dp[i][j]=max(dp[i-1][j-1]+a[i],dp[i][j]);
dp[i][0]=max(dp[i][0],dp[i-j][j]);
}
}
printf("%d",dp[n][0]);
return 0;
}
II. 波浪数
题意
给定五个数: l , r , L , R , k l,r,L,R,k~ l,r,L,R,k 要求在 [ l , r ] [l,r] [l,r]进制内找到十进制下 [ L , R ] [L,R] [L,R]范围内在 k k k 个进制下都符合数字构成为两个数字交替反复出现的数,在十进制下从小的到大输出。
思路
这道题就是需要我们模拟构造这样的数,首先我们要在最外侧枚举进制从 l l l 到 r r r,然后进行双层循环模拟组成要求的数字的每一位,每一个数字都不能超过当前进制数,首位不能为前导零,因此第一个数字从1开始枚举,并且枚举的这两个数字一定不能相同。在枚举出进制和所组成的两个数字之后就需要构造出十进制下的这个数,这里使用一个while循环逐位构造,然后我们需要一个桶数组来记录这个数满足在多少种进制下符合要求,当这个构造出的数满足在 [ L , R ] [L,R] [L,R] 范围内时,需要在桶数组中将它的出现次数加一,在最后,我们需要从小到大把 [ L , R ] [L,R] [L,R] 范围内所有数都遍历一遍桶,输出满足出现次数与 k k k 相等的数。
代码
#include<bits/stdc++.h>
using namespace std;
int a,b,l,r,x;
int mp[10000005];
int main(){
scanf("%d %d %d %d %d",&a,&b,&l,&r,&x);
for(int i=a;i<=b;i++){
for(int j=1;j<i;j++){
for(int k=0;k<i;k++){
if(k==j){
continue;
}
int w=0,cnt=0;
while(cnt<=r){
if(w%2==1){
cnt=j+i*cnt;
}
else{
cnt=k+i*cnt;
}
w++;
if(cnt>=l&&cnt<=r){
++mp[cnt];
}
}
}
}
}
for(int i=l;i<=r;i++){
if(mp[i]==x){
printf("%d\n",i);
}
}
return 0;
}
III. 无序字母对
题意
给定 n n n 个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒)。请构造一个有 ( n + 1 ) ( n + 1 ) (n+1)(n+1) (n+1)(n+1) 个字母的字符串使得每个字母对都在这个字符串中出现。
思路
实际上,这道题真正的题意就是求出给定无向图的字典序最小的欧拉路径/回路。首先是建图,这道题由于点的都是字母共 26 × 2 = 52 26\times2=52 26×2=52 个,数据可谓非常之小,直接使用邻接矩阵建,然后利用并查集判断此图是否为连通图,如不是,直接 N o S o l u t i o n No~Solution No Solution,然后判断无向图是否存在欧拉路径/回路,否则 N o S o l u t i o n No~Solution No Solution,若存在欧拉路径,以奇点为起点dfs,若存在欧拉回路,以字典序最小之点为起点dfs。
代码
#include<bits/stdc++.h>
using namespace std;
int n;
int mp[150][150];
string s;
int f[150],dep[150];
char ans[1500];
int Find(int x){
if(x==f[x]){
return x;
}
return f[x]=Find(f[x]);
}
void dfs(int x){
for(int i=60;i<=150;i++){
if(mp[x][i]){
mp[x][i]=mp[i][x]=0;
dfs(i);
}
}
ans[n--]=x;
}
int main(){
scanf("%d",&n);
for(int i=60;i<150;i++){
f[i]=i;
}
for(int i=1;i<=n;i++){
cin>>s;
mp[s[0]][s[1]]++;
mp[s[1]][s[0]]++;
int xx=Find(s[0]);
int yy=Find(s[1]);
f[xx]=yy;
dep[s[0]]++;
dep[s[1]]++;
}
int flag=0;
for(int i=60;i<150;i++){
if(dep[i]&&f[i]==i){
flag++;
}
}
if(flag!=1){
printf("No Solution");
return 0;
}
flag=0;
int p=0;
for(int i=60;i<=150;i++){
if(dep[i]&1){
flag++;
if(p==0){
p=i;
}
}
}
if(flag&&flag!=2){
printf("No Solution\n");
return 0;
}
if(p==0){
for(int i=60;i<=150;i++){
if(dep[i]){
p=i;
break;
}
}
}
dfs(p);
printf("%s",ans);
return 0;
}
IV. Points
洛谷 CF19D Points
Codeforces Points
题意
给定
n
n
n 次操作,每次操作有3种形式:
1、add x y添加点
(
x
,
y
)
(x,y)
(x,y)
2、remove x y删除点
(
x
,
y
)
(x,y)
(x,y)
3、find x y寻找在点
(
x
,
y
)
(x,y)
(x,y)右上方的点,若没有,输出
−
1
-1
−1 ,若有多个,找到最左边的点中最下面的点
代码
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod = 1e9 + 7;
const int N = 2e5 + 50;
int n;
vector<int> xl, yl;
string op;
struct qu {
int op, x, y;
} Q[N];
set<int> s[N];
struct node {
int l, r, v;
} T[N << 2];
void build(int rt, int l, int r) {
T[rt].l = l, T[rt].r = r;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
}
void update(int rt, int x, int v) {
if (T[rt].l == T[rt].r) {
T[rt].v = v;
return;
}
int mid = (T[rt].l + T[rt].r) >> 1;
if (x <= mid) {
update(rt << 1, x, v);
} else {
update(rt << 1 | 1, x, v);
}
T[rt].v = max(T[rt << 1].v, T[rt << 1 | 1].v);
}
int query(int rt, int x, int y) {
if (T[rt].l == T[rt].r) {
if (T[rt].v > y) {
return T[rt].l;
}
return -1;
}
int mid = (T[rt].l + T[rt].r) >> 1, res = -1;
// 找到大于x,y且最小的值
if (x <= mid && T[rt << 1].v > y) {
res = query(rt << 1, x, y);
}
if (~res) {
return res;
}
if (T[rt << 1 | 1].v > y) {
return query(rt << 1 | 1, x, y);
}
return -1;
}
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) {
int xt, yt;
cin >> op >> xt >> yt;
xl.push_back(xt);
yl.push_back(yt);
if (op == "add") {
Q[i] = {1, xt, yt};
} else if (op == "remove") {
Q[i] = {2, xt, yt};
} else {
Q[i] = {3, xt, yt};
}
}
sort(xl.begin(), xl.end());
sort(yl.begin(), yl.end());
// unique有序序列去重 ,返回最后一个不重复的数字在序列中的位置
xl.erase(unique(xl.begin(), xl.end()), xl.end());
yl.erase(unique(yl.begin(), yl.end()), yl.end());
for (int i = 1; i <= n; ++i) {
Q[i].x = lower_bound(xl.begin(), xl.end(), Q[i].x) - xl.begin() + 1;
Q[i].y = lower_bound(yl.begin(), yl.end(), Q[i].y) - yl.begin() + 1;
}
build(1, 1, N - 1);
for (int i = 1; i <= n; ++i) {
if (Q[i].op == 1) {
// 使用线段树维护x坐标上面最大的y坐标
// 对应的x坐标的set中,存储了当前x坐标对应的所有y坐标
s[Q[i].x].insert(Q[i].y);
int p = *s[Q[i].x].rbegin();
update(1, Q[i].x, p);
} else if (Q[i].op == 2) {
// 集合中应该删除当前这个点的y坐标
s[Q[i].x].erase(Q[i].y);
int p;
if (s[Q[i].x].size() == 0) {
p = 0;
} else {
// 获取一个新的最大值
p = *s[Q[i].x].rbegin();
}
update(1, Q[i].x, p);
} else {
int p = query(1, Q[i].x + 1, Q[i].y);
if (p == -1) {
cout << p << endl;
} else {
int py = *s[p].upper_bound(Q[i].y);
cout << xl[p - 1] << ' ' << yl[py - 1] << endl;
}
}
}
return 0;
}

1072

被折叠的 条评论
为什么被折叠?



