今天排名一般,但是却感觉不错,也是可以的
7.29场链接
A题:
题意:01背包问题,要求最优情况是重量和体积都最大的情况
解法:和之前的“劲歌金曲”那道题挺像的,就是设计一个最优状态,只有当这个状态比当前状态更优的时候我们才进行状态转移,状态这个词最为重要
注意压缩完空间后的最终状态所处的位置
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int w[1005], v[1005];
typedef long long ll;
struct node {
int w, v;
bool operator < (const node & rhs)const {
return w < rhs.w || (w == rhs.w&&v < rhs.v);
}
}dp[100005];
int main() {
int T; scanf("%d", &T);
while (T--) {
int n, c; scanf("%d%d", &n, &c);
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++)scanf("%d", &w[i]);
for (int j = 1; j <= n; j++)scanf("%d", &v[j]);
for (int i = 1; i <= n; i++) {
for (int j = c; j >= v[i]; j--) {
node tmp;
tmp.v = dp[j - v[i]].v + v[i];
tmp.w = dp[j - v[i]].w + w[i];
if (dp[j] < tmp)dp[j] = tmp;
}
}
printf("%d %d
", dp[c].w, dp[c].v);
}
return 0;
}
D题:
题意:有n堆石子,每次要求把重量最小的两堆进行合并,之后会有m个询问,问第几次操作完中某堆石子的序号
解法:
哈夫曼树,并查集,因为当这个石子和其他石子合并的时候,他们就相当于属于一个集合了,那么之后对集合中一个元素的修改就相当于修改其所有的元素
#include<cstdio>
#include<queue>
#include<functional>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int set[1000000];
int findSet(int x) {
if (x == set[x]) return x;
else return set[x] = findSet(set[x]);
}
void unionSet(int x, int y) {//将y设置为x的父亲
int fx = findSet(x);
int fy = findSet(y);
if (fx != fy) {
set[fx] = fy;
}
}
struct node {
ll w;
int id;
bool operator < (const node& rhs)const {
return w > rhs.w || (w == rhs.w&&id > rhs.id);
}
};
priority_queue <node> q;
queue<pair<int, int>>ask;
int main() {
int T; scanf("%d", &T);
while(T--){
while (!q.empty())q.pop();
while (!ask.empty())ask.pop();
memset(set, 0, sizeof(set));
int n, m; scanf("%d%d", &n, &m);
ll tmp;
for (int i = 1; i <= n; i++) {
scanf("%lld", &tmp);
q.push(node{ tmp,i });//编号为i的稀泥的重量为tmp
set[i] = i;
}
for (int i = 1; i <= m; i++) {
pair<int, int>tmp;
scanf("%d%d", &tmp.first, &tmp.second);
ask.push(tmp);
}
int now = 0;
while (ask.size()&&q.size() > 1) {
node x1 = q.top(); q.pop();
node x2 = q.top(); q.pop();
if (x1.id > x2.id)swap(x1.id, x2.id);
unionSet(x2.id, x1.id);
q.push(node{ x1.w + x2.w,findSet(x1.id) });
now++;
while (ask.size()&&now == ask.front().first) {
printf("%d
", findSet(ask.front().second));
ask.pop();
}
}
}
return 0;
}
codeforces contest 1009
B题:
题意:给定一个数列,这个数列里面只有0,1,2,有两种操作,将相邻的01对换位置,将相邻的12调换位置,2和0不能调换位置,每种操作可以变无数次,问经过多种这样子的操作后字典序最小的序列是什么样子的。
解法:由于2和0的位置是不能调换的,所以我们就把所有的1单独拿出来,然后再把所有的1放到第一个2前面就行了,很好的思路题
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
char s[100000 + 5];
int main() {
scanf("%s", s);
int len = strlen(s), pos=-1, num = 0;
for (int i = 0; i < len; i++) {
if (s[i] == '2'&& pos==-1) pos = i;
if (s[i] == '1') { num++; s[i] = '#'; }
}
for (int i = 0; i < len; i++) {
if (s[i] == '#')continue;
if (i == pos)for (int k = 0; k < num; k++)printf("1");
printf("%c", s[i]);
}
if(pos==-1)for (int k = 0; k < num; k++)printf("1");
return 0;
}
C题:
题意:给定一个序列,每次给你两个数,要求将序列里面的每个数都加上x,之后选择一个位置j,将所有的数加上d*dist(i,j),问怎样选择j可以使得最后所有的数的平均数最大
解法:推一下公式就行了,要注意的是double的用法
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
int a[100000 + 5];
int main() {
ll n, m; scanf("%I64d%I64d", &n, &m);
ll x, d, pos;
ll ans = 0;
for (int i = 0; i < m; i++) {
scanf("%I64d%I64d", &x, &d);
ans += ll(n)*x;
if (d > 0) { ans += d *n*(n - 1) / 2; }
else ans += d*(n - (n + 1) / 2)*((n + 1) / 2);
}
printf("%.15f
", double(ans)/n);
return 0;
}