Codeforces Round #611 (Div. 3)
A - Minutes Before the New Year
题意:问现在的时间距离0点0分有多少分钟,提问的时间在0点0分之前,且不是0点0分。
题解:?
void test_case() {
int h, m;
scanf("%d%d", &h, &m);
int ans = 0;
if(m == 0)
ans += (24 - h) * 60;
else {
ans += (23 - h) * 60;
ans += 60 - m;
}
printf("%d\n", ans);
}
B - Candies Division
题意:把n颗糖分给k个人,可以留下一些糖,要求分配的最大值与最小值的差不超过1(尽可能平均),且比最小值多1的人数量不超过一半,在此基础上尽可能多分配。
题解:先平均分配下整,然后零头加上去。
void test_case() {
int n, k;
scanf("%d%d", &n, &k);
int a = n / k;
int ans = k * a;
int surplus = min(k / 2, n - ans);
ans += surplus;
printf("%d\n", ans);
}
C - Friends and Gifts
题意:每个人必须恰好给出1颗糖果,恰好得到1颗糖果,p[i]表示他要给谁,0表示他等待分配,输入保证不矛盾。分配每个人要给谁,使得满足题目约束。
题解:这张图就是若干条链和若干个环,环不影响直接去掉,然后链全部首尾相连。
int s[200005];
int p[200005];
int h[200005];
int t[200005];
int cnt;
int dfs(int u) {
if(s[u] != 0)
return dfs(s[u]);
else
return u;
}
void test_case() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &p[i]);
s[p[i]] = i;
}
cnt = 0;
for(int i = 1; i <= n; ++i) {
if(p[i] == 0) {
if(s[i] == 0) {
++cnt;
h[cnt] = i;
t[cnt] = i;
} else {
++cnt;
h[cnt] = i;
t[cnt] = dfs(i);
}
//printf("h=%d\n",h[cnt]);
//printf("t=%d\n",t[cnt]);
}
}
int j = 1;
for(int i = 1; i <= n; ++i) {
if(p[i] == 0) {
p[i] = t[++j];
if(j > cnt)
p[i] = t[1];
}
}
for(int i = 1; i <= n; ++i)
printf("%d%c", p[i], " \n"[i == n]);
}
D - Christmas Trees
题意:数轴上有n棵圣诞树,位置分别是xi。给m个人分配位置,使得所有人到离他最近的圣诞树的距离的和最小。
题解:直接贪心往每棵树两边加人即可。用了一个丑陋的实现,先二分确定可以加的值,然后再加。
int n, m;
int x[200005];
bool check(int mlen) {
ll sumpoints = 2 * mlen;
for(int i = 2; i <= n; ++i) {
sumpoints += min(x[i] - x[i - 1] - 1, 2 * mlen);
if(sumpoints >= m) {
//printf("check mlen=%d, suc.\n", mlen);
return true;
}
}
if(sumpoints >= m) {
//printf("check mlen=%d, suc.\n", mlen);
return true;
}
//printf("check mlen=%d, fail.\n", mlen);
return false;
}
int ans[200005];
priority_queue<pii> pq;
ll answer(int mlen) {
for(int j = 1; j <= mlen; ++j)
pq.push({-j, x[1] - j});
for(int j = 1; j <= mlen; ++j)
pq.push({-j, x[n] + j});
for(int i = 1; i < n; ++i) {
if(x[i + 1] - x[i] - 1 > 2 * mlen) {
for(int j = 1; j <= mlen; ++j)
pq.push({-j, x[i] + j});
for(int j = 1; j <= mlen; ++j)
pq.push({-j, x[i + 1] - j});
} else {
int dis = (x[i + 1] - x[i]);
for(int j = 1; ; ++j) {
if(x[i] + j == x[i + 1])
break;
pq.push({-(min(j, dis - j)), x[i] + j});
}
}
}
ll res = 0;
for(int j = 1; j <= m; ++j) {
ans[j] = pq.top().second;
res += (-pq.top().first);
//cout << "x=" << pq.top().second << endl;
//cout << "d=" << (-pq.top().first) << endl;
pq.pop();
}
while(pq.size())
pq.pop();
return res;
}
ll bs() {
int l = 1, r = m;
while(1) {
int mid = l + r >> 1;
if(l == mid) {
if(check(l))
return answer(l);
assert(check(r));
return answer(r);
}
if(check(mid))
r = mid;
else
l = mid + 1;
}
}
void test_case() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
scanf("%d", &x[i]);
if(n == 1) {
ll sum = 0;
int l = x[1], r = x[1];
for(int i = 1; i <= m; ++i) {
if(i & 1) {
ans[i] = --l;
sum += x[1] - l;
} else {
ans[i] = ++r;
sum += r - x[1];
}
}
printf("%lld\n", sum);
sort(ans + 1, ans + 1 + m);
for(int i = 1; i <= m; ++i)
printf("%d%c", ans[i], " \n"[i == m]);
return;
}
sort(x + 1, x + 1 + n);
printf("%lld\n", bs());
sort(ans + 1, ans + 1 + m);
for(int i = 1; i <= m; ++i)
printf("%d%c", ans[i], " \n"[i == m]);
}
但是其实可以存每棵树的l指针和r指针,当某棵树的r指针遇到另一棵树的l指针的时候就把这两个指针去掉。不过这样写起来不见得比二分好写。先二分的原因是可以避免把过多的点插进优先队列。
E - New Year Parties
题意:有n个人,每个人可以左移1、右移1或者留在原地。求最小覆盖多少个点和最大覆盖多少个点。
题解:很明显满足dp的性质。只是很容易漏掉一些转移。
int n;
int x[200005];
int dp[200005][8];
void test_case() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &x[i]);
++x[i];
}
sort(x + 1, x + 1 + n);
//xi value range [2,n+1]
//can move to value range [1,n+2]
//MIN
memset(dp, INF, sizeof(dp));
dp[0][0] = 0;
for(int i = 1; i <= n; ++i) {
if(i == 1 || x[i] == x[i - 1]) {
//move to left
dp[i][4] = min(dp[i - 1][4], dp[i - 1][0] + 1);
dp[i][5] = min(dp[i - 1][5], dp[i - 1][1] + 1);
dp[i][6] = min(dp[i - 1][6], dp[i - 1][2] + 1);
dp[i][7] = min(dp[i - 1][7], dp[i - 1][3] + 1);
//stay here
dp[i][2] = min(dp[i - 1][2], dp[i - 1][0] + 1);
dp[i][3] = min(dp[i - 1][3], dp[i - 1][1] + 1);
dp[i][6] = min(dp[i][6], min(dp[i - 1][6], dp[i - 1][4] + 1));
dp[i][7] = min(dp[i][7], min(dp[i - 1][7], dp[i - 1][5] + 1));
//move to right
dp[i][1] = min(dp[i - 1][1], dp[i - 1][0] + 1);
dp[i][3] = min(dp[i][3], min(dp[i - 1][3], dp[i - 1][2] + 1));
dp[i][5] = min(dp[i][5], min(dp[i - 1][5], dp[i - 1][4] + 1));
dp[i][7] = min(dp[i][7], min(dp[i - 1][7], dp[i - 1][6] + 1));
} else {
if(x[i] == x[i - 1] + 1) {
//move to left
dp[i][4] = min(min(dp[i - 1][2], dp[i - 1][6]), min(dp[i - 1][0], dp[i - 1][4]) + 1);
dp[i][6] = min(min(dp[i - 1][3], dp[i - 1][7]), min(dp[i - 1][1], dp[i - 1][5]) + 1);
//stay here
dp[i][2] = min(min(dp[i - 1][1], dp[i - 1][5]), min(dp[i - 1][0], dp[i - 1][4]) + 1);
dp[i][6] = min(dp[i][6], min(min(dp[i - 1][3], dp[i - 1][7]), min(dp[i - 1][2], dp[i - 1][6]) + 1));
//move to right
dp[i][1] = min(dp[i - 1][0], dp[i - 1][4]) + 1;
dp[i][3] = min(dp[i - 1][1], dp[i - 1][5]) + 1;
dp[i][5] = min(dp[i - 1][2], dp[i - 1][6]) + 1;
dp[i][7] = min(dp[i - 1][3], dp[i - 1][7]) + 1;
} else if(x[i] == x[i - 1] + 2) {
//move to left
dp[i][4] = min(min(dp[i - 1][1], dp[i - 1][3]), min(dp[i - 1][5], dp[i - 1][7]));
for(int j = 0; j <= 7; j += 2)
dp[i][4] = min(dp[i][4], dp[i - 1][j] + 1);
//stay here
for(int j = 0; j <= 7; j += 2)
dp[i][2] = min(dp[i][2], dp[i - 1][j] + 1);
for(int j = 1; j <= 7; j += 2)
dp[i][6] = min(dp[i][6], dp[i - 1][j] + 1);
//move to right
for(int j = 0; j <= 7; j += 2)
dp[i][1] = min(dp[i][1], dp[i - 1][j] + 1);
for(int j = 1; j <= 7; j += 2)
dp[i][5] = min(dp[i][5], dp[i - 1][j] + 1);
} else {
//move to left
for(int j = 0; j <= 7; ++j)
dp[i][4] = min(dp[i][4], dp[i - 1][j] + 1);
//stay here
for(int j = 0; j <= 7; ++j)
dp[i][2] = min(dp[i][2], dp[i - 1][j] + 1);
//move to right
for(int j = 0; j <= 7; ++j)
dp[i][1] = min(dp[i][1], dp[i - 1][j] + 1);
}
}
/*for(int j = 0; j <= 7; ++j)
printf("dp[%d][%d]=%d\n", i, j, dp[i][j]);
printf("\n");*/
}
int ans1 = INF;
for(int j = 0; j <= 7; ++j)
ans1 = min(ans1, dp[n][j]);
//MAX
memset(dp, -INF, sizeof(dp));
dp[0][0] = 0;
for(int i = 1; i <= n; ++i) {
if(i == 1 || x[i] == x[i - 1]) {
//move to left
dp[i][4] = max(dp[i - 1][4], dp[i - 1][0] + 1);
dp[i][5] = max(dp[i - 1][5], dp[i - 1][1] + 1);
dp[i][6] = max(dp[i - 1][6], dp[i - 1][2] + 1);
dp[i][7] = max(dp[i - 1][7], dp[i - 1][3] + 1);
//stay here
dp[i][2] = max(dp[i - 1][2], dp[i - 1][0] + 1);
dp[i][3] = max(dp[i - 1][3], dp[i - 1][1] + 1);
dp[i][6] = max(dp[i][6], max(dp[i - 1][6], dp[i - 1][4] + 1));
dp[i][7] = max(dp[i][7], max(dp[i - 1][7], dp[i - 1][5] + 1));
//move to right
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + 1);
dp[i][3] = max(dp[i][3], max(dp[i - 1][3], dp[i - 1][2] + 1));
dp[i][5] = max(dp[i][5], max(dp[i - 1][5], dp[i - 1][4] + 1));
dp[i][7] = max(dp[i][7], max(dp[i - 1][7], dp[i - 1][6] + 1));
} else {
if(x[i] == x[i - 1] + 1) {
//move to left
dp[i][4] = max(max(dp[i - 1][2], dp[i - 1][6]), max(dp[i - 1][0], dp[i - 1][4]) + 1);
dp[i][6] = max(max(dp[i - 1][3], dp[i - 1][7]), max(dp[i - 1][1], dp[i - 1][5]) + 1);
//stay here
dp[i][2] = max(max(dp[i - 1][1], dp[i - 1][5]), max(dp[i - 1][0], dp[i - 1][4]) + 1);
dp[i][6] = max(dp[i][6], max(max(dp[i - 1][3], dp[i - 1][7]), max(dp[i - 1][2], dp[i - 1][6]) + 1));
//move to right
dp[i][1] = max(dp[i - 1][0], dp[i - 1][4]) + 1;
dp[i][3] = max(dp[i - 1][1], dp[i - 1][5]) + 1;
dp[i][5] = max(dp[i - 1][2], dp[i - 1][6]) + 1;
dp[i][7] = max(dp[i - 1][3], dp[i - 1][7]) + 1;
} else if(x[i] == x[i - 1] + 2) {
//move to left
for(int j = 1; j <= 7; j += 2)
dp[i][4] = max(dp[i][4], dp[i - 1][j]);
for(int j = 0; j <= 7; j += 2)
dp[i][4] = max(dp[i][4], dp[i - 1][j] + 1);
//stay here
for(int j = 0; j <= 7; j += 2)
dp[i][2] = max(dp[i][2], dp[i - 1][j] + 1);
for(int j = 1; j <= 7; j += 2)
dp[i][6] = max(dp[i][6], dp[i - 1][j] + 1);
//move to right
for(int j = 0; j <= 7; j += 2)
dp[i][1] = max(dp[i][1], dp[i - 1][j] + 1);
for(int j = 1; j <= 7; j += 2)
dp[i][5] = max(dp[i][5], dp[i - 1][j] + 1);
} else {
//move to left
for(int j = 0; j <= 7; ++j)
dp[i][4] = max(dp[i][4], dp[i - 1][j] + 1);
//stay here
for(int j = 0; j <= 7; ++j)
dp[i][2] = max(dp[i][2], dp[i - 1][j] + 1);
//move to right
for(int j = 0; j <= 7; ++j)
dp[i][1] = max(dp[i][1], dp[i - 1][j] + 1);
}
}
/*for(int j = 0; j <= 7; ++j)
printf("dp[%d][%d]=%d\n", i, j, dp[i][j]);
printf("\n");*/
}
int ans2 = 0;
for(int j = 0; j <= 7; ++j)
ans2 = max(ans2, dp[n][j]);
printf("%d %d\n", ans1, ans2);
}
好像有贪心的写法?
最小的贪心:首先把最左侧的元素往右移,然后记录当前的lst(最右)位置,假如某个元素x<=lst+1说明他可以通过某些移动叠到lst中,跳过这种情况,否则一定断开了,递归到小规模的问题。
最大的贪心:首先把最左侧的元素往左移,然后记录当前的lst(最右位置),假如某个元素xlst,则可以把x右移;假如某个元素xlst+1那么它可以不移动,其他情况说明断开了,递归到小规模的问题。
int n;
int x[200005];
int solve_min() {
int ans = 0;
int lst = -INF;
for(int i = 1; i <= n; ++i) {
if(x[i] <= lst + 1)
continue;
else {
++ans;
lst = x[i] + 1;
}
}
return ans;
}
int solve_max() {
int ans = 0;
int lst = -INF;
for(int i = 1; i <= n; ++i) {
if(x[i] <= lst - 1)
continue;
else {
++ans;
if(x[i] == lst)
lst = x[i] + 1;
else if(x[i] == lst + 1)
lst = x[i];
else
lst = x[i] - 1;
}
}
return ans;
}
void test_case() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &x[i]);
++x[i];
}
sort(x + 1, x + 1 + n);
printf("%d %d\n", solve_min(), solve_max());
}