POJ
水题
3299 18-06-07
- 声明变量要放在while里面,不然会有上一次循环的残留
%.1f
可以自动做round
#include <iostream>
#include <string>
#include <sstream>
#include <cmath>
#include <iomanip>
#include <cstdio>
int main()
{
std::string input;
std::string first, second;
while (true) {
double a, b, humidex = -1000, temperature = -1000, h = -1000, e, dewpoint;
std::getline(std::cin, input);
if (input == "E")
break;
std::stringstream ss;
ss << input;
ss >> first >> a >> second >> b;
for (int i = 0; i < 2; ++i) {
if (first == "D") {
dewpoint = a;
e = 6.11 * std::exp (5417.7530 * ((1 / 273.16) - (1 / (dewpoint + 273.16))));
h = 0.5555 * (e - 10.0);
} else if (first == "T") {
temperature = a;
} else {
humidex = a;
}
first = second;
a = b;
}
if (temperature > -100 && h > -100)
humidex = temperature + h;
else if (humidex > -100 && temperature > -100) {
h = humidex - temperature;
e = h / 0.5555 + 10.0;
dewpoint = 1 / (1 / 273.16 - std::log(e / 6.11) / 5417.7530) - 273.16;
} else {
temperature = humidex - h;
}
printf("T %.1f D %.1f H %.1f\n", temperature, dewpoint, humidex);
}
}
初期
基本算法
枚举
1753 18-06-09
长时间不写脑子是不灵光了。首先一点,任何一个硬币,在整个过程中被翻动偶数次都和没翻一样,无论这几次之前是否被旁边的翻动影响,那么自然所有奇数次都与1次等效,所以整个过程就可以枚举为 216 2 16 种情况,可以接受。
#include <iostream>
int a[4][4];
int min_count = 20;
bool found = false;
void flip(int x, int y)
{
if (x < 0 || x > 3 || y < 0 || y > 3)
return;
a[x][y] = 1 - a[x][y];
}
int b[16] = { 0 };
void flip_wrapper(int index)
{
int x = index / 4;
int y = index - x * 4;
flip(x, y);
flip(x, y - 1);
flip(x, y + 1);
flip(x - 1, y);
flip(x + 1, y);
}
void test(int index, int flip_count)
{
if (index == 16)
{
int sum = 0;
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
sum += a[i][j];
if (sum == 0 || sum == 16)
{
if (flip_count < min_count)
min_count = flip_count;
found = true;
// for (int i = 0; i < 16; ++i)
// std::cout << b[i] << " ";
// std::cout << std::endl;
}
return;
}
int increment = 0; // increment是为了消除有可能当前测试硬币在其他递归树中被翻动的影响
if (b[index] == 1)
increment = 1;
test(index + 1, flip_count + increment);
flip_wrapper(index);
b[index] = 1 - b[index];
test(index + 1, flip_count + 1 - increment);
}
int main()
{
std::string s;
for (int i = 0; i < 4; ++i)
{
std::cin >> s;
for (int j = 0; j < 4; ++j)
{
if (s[j] == 'w')
a[i][j] = 0;
else
a[i][j] = 1;
}
}
test(0, 0);
if (found)
std::cout << min_count << std::endl;
else
std::cout << "Impossible" << std::endl;
}
贪心
1328 18-06-16
因为搞目标检测的事情耽误了好多时间。因为看的博客,所以一开始就知道是用贪心做,所以一开始一直在想如何在二维上做贪心之类的,比如先考虑高度之类的,但是这样很容易举出很多反例。因此关键还是在于,将二维转换为一维,其实题目中已经比较明显了,雷达是顺着一维的海岸线布置的,所以应该从每个海岛可以被海岸线上多少范围内的雷达站探测到出发,转换为一维的贪心过程。每个海岛对应的雷达站范围是 [x−d2−y2−−−−−−√,x+d2−y2−−−−−−√] [ x − d 2 − y 2 , x + d 2 − y 2 ] ,infeasible的情况自然也蕴含其中了。所以问题变成了需要多少个点才能让这些线段范围里都至少有一个点。这时候就可以使用贪心策略,按照座标升序排列,初始化重合段等于一个无效区间,如果下一段和重合段有重合,则重合段调整大小,雷达站数量不变,如果无重合,重合段等于下一段,雷达站数量+1。
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <limits>
bool intersect(double &l, double &r, double &ll, double &rr)
{
if (l < ll)
{
if (r >= ll)
{
l = ll;
if (r > rr)
r = rr;
return true;
}
else
return false;
}
else
{
if (l <= rr)
{
if (r > rr)
r = rr;
return true;
}
else
return false;
}
}
struct point
{
double left;
double right;
};
bool comp(const point &a, const point &b)
{
if (a.left < b.left)
return true;
else if (a.left == b.left)
return a.right < b.right;
else
return false;
}
int main()
{
int n, d, count = 1, x, y;
point p[1005];
while (scanf("%d %d", &n, &d))
{
bool infeasible = false;
if (n == 0 && d == 0)
break;
for (int i = 0; i < n; ++i)
{
scanf("%d %d", &x, &y);
if (y > d)
{
infeasible = true;
continue;
}
double dd = sqrt(d*d - y*y);
p[i].left = x - dd;
p[i].right = x + dd;
}
if (infeasible)
printf("Case %d: -1\n", count);
else
{
int radar = 0;
std::sort(p, p+n, comp);
double l = std::numeric_limits<double>::max(), r = l;
for (int i = 0; i < n; ++i)
{
if (!intersect(l, r, p[i].left, p[i].right))
{
radar++;
l = p[i].left;
r = p[i].right;
}
}
printf("Case %d: %d\n", count, radar);
}
count++;
}
}
构造法
3295 18-06-23
#include <cstdio>
#include <cstring>
char wff[105];
int input[32][5];
int g_index = 0;
struct mystack {
int s[105];
int top;
mystack() {
top = 0;
};
void push(int ch) {
s[top] = ch;
++top;
};
int pop() {
--top;
return s[top];
};
};
void init(int lvl, int buf[])
{
if (lvl == 5)
{
for (int i = 0; i < 5; ++i)
input[g_index][i] = buf[i];
++g_index;
return;
}
buf[lvl] = 0;
init(lvl+1, buf);
buf[lvl] = 1;
init(lvl+1, buf);
}
int get_num(int idx, char ch)
{
return input[idx][ch-'p'];
}
int op(char o, int a, int b)
{
if (o == 'K')
return a & b;
if (o == 'A')
return a | b;
if (o == 'N')
return !a;
if (o == 'C')
return (!a) | b;
if (o == 'E')
return (a & b) || (!a & !b);
}
int main()
{
int buf[5];
init(0, buf);
while (true)
{
scanf("%s", wff);
if (wff[0] == '0')
break;
int length = strlen(wff);
mystack s[32];
char ch;
int w, x;
while(length--)
{
ch = wff[length];
for (int i = 0; i < 32; ++i)
{
if (ch >= 'p' && ch <= 't')
s[i].push(get_num(i, ch));
else
{
w = s[i].pop();
if (ch != 'N')
x = s[i].pop();
s[i].push(op(ch, w, x));
}
}
}
int sum = 0;
for (int i = 0; i < 32; ++i)
sum += s[i].pop();
if (sum == 32) // 恒为1,不是所有输出一样
printf("tautology\n");
else
printf("not\n");
}
}
模拟法
1068 18-07-12
自闭。普通模拟题,P序列转字符串自然很好做,只要以增量的形式添加左括号就行了,主要是解码为W序列上,用了一个比较蠢但是逻辑清楚的方法,将左括号以数字1入栈,每次遇到右括号的时候,弹栈并打印该值,再将栈内所有元素加1,相当于记录了栈内括号的作用域中发生的弹栈行为的数量(即碰到右括号的数量)。
一开始的结果是Memory 680K 0MS。后来觉得可以在字符串上做一些优化,所以将std::string
和+=
等操作都换成了对预留空间的char
数组的操作,勉强把内存减少到了356K,离前面的大神还有很远。
#include <cstdio>
#include <iostream>
#include <string>
struct mystack
{
int s[25];
int top;
mystack()
{
top = 0;
};
void push(int num)
{
s[top] = num;
++top;
};
int pop()
{
return s[--top];
};
void increase()
{
for (int i = 0; i < top; ++i)
++s[i];
};
void reset()
{
top = 0;
};
};
int main()
{
int t, n, temp;
char s[50];
mystack sta;
scanf("%d", &t);
while (t--)
{
int init = 0, index = 0;
scanf("%d", &n);
// std::string s = "";
for (int i = 0; i < n; ++i)
{
scanf("%d", &temp);
for (int j = 0; j < temp - init; ++j)
s[index++] = '(';
s[index++] = ')';
init = temp;
}
// std::cout << s << std::endl;
for (int j = 0; j < index; ++j)
{
if (s[j] == '(')
sta.push(1);
else
{
printf("%d ", sta.pop());
sta.increase();
}
}
printf("\n");
sta.reset();
}
}