签到题,考虑数据范围达到 109 ,即思考 O(1) 时间内的解法。
具体有
//NOJ 2210
#include
int main()
{
int l, r;
scanf("%d%d", &l, &r);
if(l == r) printf("%d\n", l);
else printf("2\n");
return 0;
}
博弈类题,此题中抽卡顺序其实是有MiaoMiao主导的,所以考虑解法时必须以 bi 的值为排序。
从结果考虑,将所有的卡片按 bi 逆序排序后,当MiaoMiao抽到第 i 张卡片时,Bird能在这些卡片中取走 i+12 张卡片,当然Bird需要优先拿走 ai+bi 较大的卡片。
由此我们可以用一个以 ai+bi 值为key的优先队列,将所有卡片按 bi 值排序后,依次插入优先队列,当队列中的卡片数量超过 i+12 时,弹出 ai+bi 最小的卡片。最后对留在队列中的卡片求和。
//NOJ 2211
#include
#include
#include
#include
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;
}
类似动态规划思想的题目。如果用背包的思想完成,那么时间复杂度将达到 O(n2) 超时。
注意题目中要求只选择两件物品所以我们可以以 K/2 为界限对物品进行分组。
首先,将物品按物品费用排序。记录所有 ci≤K/2 的物品价值,由于可能有相同费用的物品,有
//NOJ 2212
#include
#include
#include
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;
}
公式题,读懂题意就能推出正确的公式。
类似于一种规划问题,由公式 ki=ki−1×b/(wi−1+li)
由于当 i≥1 时, wi=b ,所以我们可以推出在洗 m(m≤n) 次服时的 km
//NOJ 2213
#include
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;
}
数据范围 (1≤n,m≤8) ,暴力模拟或数位dp即可。
以下方法时间复杂度 O(n∗22m) ,使用到位运算(并不怎么优秀)
//NOJ 2214
#include
#include
#include
#include
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<1;
for(int i = 0; i < (1<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;
}