[NOJ 2010]数因子
签到题,考虑数据范围达到109,即思考O(1)时间内的解法。
具体有
ans={2 (l≠r)l (l==r)
当然,存在多个解的情况(由于没有特判,可能有部分玄学方法被判错)
//NOJ 2210
#include <cstdio>
int main()
{
int l, r;
scanf("%d%d", &l, &r);
if(l == r) printf("%d\n", l);
else printf("2\n");
return 0;
}
[NOJ 2011] 抽卡片
博弈类题,此题中抽卡顺序其实是有MiaoMiao主导的,所以考虑解法时必须以bi的值为排序。
从结果考虑,将所有的卡片按bi逆序排序后,当MiaoMiao抽到第i张卡片时,Bird能在这些卡片中取走
由此我们可以用一个以ai+bi值为key的优先队列,将所有卡片按bi值排序后,依次插入优先队列,当队列中的卡片数量超过i+12时,弹出ai+bi最小的卡片。最后对留在队列中的卡片求和。
//NOJ 2211
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 100005;
priority_queue<int,vector<int>,greater<int> > q;
struct Card
{
int a, b, id;
bool operator <(const Card& rh)const{return (b > rh.b)||(b == rh.b && id < rh.id);}
}c[N];
int main()
{
int n;
ll sum = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++){
c[i].id = i;
scanf("%d", &c[i].a);
}
for(int i = 1; i <= n; i++){
scanf("%d", &c[i].b);
c[i].a += c[i].b;
sum -= c[i].b;
}
sort(c + 1, c + 1 + n);
for(int i = 1; i <= n; i++){
q.push(c[i].a);
if(q.size() > (i+1)/2)
q.pop();
}
while(!q.empty()){
sum += q.top();
q.pop();
}
printf("%I64d\n", sum);
return 0;
}
[NOJ 2012] 选礼物
类似动态规划思想的题目。如果用背包的思想完成,那么时间复杂度将达到O(n2)超时。
注意题目中要求只选择两件物品所以我们可以以K/2为界限对物品进行分组。
首先,将物品按物品费用排序。记录所有ci≤K/2的物品价值,由于可能有相同费用的物品,有
dp[ci]=max(dp[ci],pi)
同时我们记录这些物品中最大和次大的价值(如果存在)ans=pMax+pMax2
之后,使dp数组最大化,即dp[i]=max(dp[i−1],dp[i])(i≥1)
之后枚举ci>K/2的物品,即ans=max(ans,p[i]+dp[K−c[i]])(dp[K−c[i]]>0)(等于0时表示没有不超过K−c[i] )
//NOJ 2212
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
struct Gift
{
int c, p;
bool operator< (const Gift& rh)const{return c < rh.c;}
}g[N];
int pre[N];
int main()
{
int n, k;
scanf("%d%d", &n, &k);
for(int i = 0; i < n; i++)
scanf("%d%d", &g[i].c, &g[i].p);
sort(g, g + n);
int smax, smax2, p, ans;
ans = smax = smax2 = 0;
for(p = 0; p < n; p++){
if(g[p].c > k / 2) break;
pre[g[p].c] = max(pre[g[p].c], g[p].p);
if(g[p].p > smax){smax2 = smax; smax = g[p].p;}
else if(g[p].p > smax2) smax2 = g[p].p;
}
if(smax && smax2) ans = smax + smax2;
for(int i = 1; i <= k/2; i++)
pre[i] = max(pre[i], pre[i - 1]);
for(; p < n; p++)
if(g[p].c < k && pre[k - g[p].c])
ans = max(ans, g[p].p + pre[k - g[p].c]);
printf("%d\n", ans);
return 0;
}
[NOJ 2013] 洗衣服
公式题,读懂题意就能推出正确的公式。
类似于一种规划问题,由公式ki=ki−1×b/(wi−1+li)
由于当i≥1时,wi=b,所以我们可以推出在洗m(m≤n)次服时的km
minkm=k0×ba+l1×bb+l2×⋯×bb+lm
s.t.⎧⎩⎨⎪⎪⎪⎪∑li≤Lli>0l1+a≥b
由此,当L+a>b时,可将ba+l1化为bb+(l1−b+a),由不等式性质得当l1−b+a=l2=⋯=lm时km最小,又km=f(m)单调递减,当m=n时,kn取到最小值。
//NOJ 2213
#include <cstdio>
int main()
{
int a, b, k, l, n;
scanf("%d%d%d%d%d", &a, &b, &k, &l, &n);
if(l <= b - a) printf("%.4f\n", (double)k);
else{
l -= b - a;
double ans = k;
for(int i = 0; i < n; i++)
ans *= (1.0 * b * n) / (b * n + 1.0 * l);
printf("%.4f\n", ans);
}
return 0;
}
[NOJ 2014] 看电视
数据范围(1≤n,m≤8),暴力模拟或数位dp即可。
以下方法时间复杂度O(n∗22m),使用到位运算(并不怎么优秀)
//NOJ 2214
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 100, M = 8;
int mat[N + 5], cnt[1 << M | 1], exp[1 << M | 1];
int dp[N + 5][1 << M | 1][1 << M | 1];
int n, m;
int expend(int x)
{
int ret = 0;
for(int i = 0; i < m; i++){
if((x >> i) & 1){
ret |= (1 << i);
if(i > 0) ret |= (1 << (i - 1));
if(i < m - 1) ret |= (1 << (i + 1));
}
}
return ret;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i < (1<<m); i++)
cnt[i] = cnt[i - (i & (-i))] + 1;
for(int i = 0; i < (1<<m); i++)
exp[i] = expend(i);
for(int i = 1; i <= n; i++){
int x = 0;
getchar();
for(int j = 1; j <= m; j++){
char ch;
scanf("%c", &ch);
x <<= 1;
if(ch == '*') x |= 1;
}
mat[i] = x;
}
memset(dp, -1, sizeof(dp));
for(int i = 0; i < (1 << m); i++)
if((exp[i] | mat[1]) == mat[1])
dp[1][0][i] = cnt[i];
for(int i = 2; i <= n; i++){
for(int j = 0; j < (1 << m); j++)
for(int k = 0; k < (1 << m); k++)
if(dp[i-1][j][k] != -1)
for(int p = 0; p < (1 << m); p++)
if(((j | exp[k] | p) == mat[i - 1]) && (((k | exp[p]) | mat[i]) == mat[i])){
if(dp[i][k][p] == -1) dp[i][k][p] = dp[i-1][j][k] + cnt[p];
else dp[i][k][p] = min(dp[i][k][p], dp[i-1][j][k] + cnt[p]);
}
}
int ans = 0x3f3f3f3f;
for(int i = 0; i < (1 << m); i++)
for(int j = 0; j < (1 << m); j++)
if(dp[n][i][j] != -1 && ((i | exp[j]) == mat[n])){
ans = min(ans, dp[n][i][j]);
}
if(ans == 0x3f3f3f3f) ans = -1;
printf("%d\n", ans);
return 0;
}