我说啊,你这个long long 写成 int 导致 WA,== 写成 = WA,多少次啦!为啥不长点记性?!
蒟蒻对于这本书的会写的课后习题编了菜菜的代码,不会写的请教了大佬然后整理了代码,并胆大包天居然敢把部分例题改成了菜菜的代码,不过这些代码都是可以在相应OJ上提交并通过的(注明WA的除外)。
目录
第二章
2.1
dfs
bfs
穷竭搜索
2.2
2.3
2.4
2.5
2.6
1. POJ 1979: Red and Black
#include
#include
using namespace std;
const int MAXH = 25;
char field[MAXH][MAXH];
int W, H;
int res;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, -1, 0, 1};
typedef pair P;
void bfs()
{
queue que;
for(int i = 0; i < H; i++)
for(int j = 0; j < W; j++){
if(field[i][j] == '@'){
que.push(P(i, j));
field[i][j] = '#';
res++;
}
}
while(que.size()){
P p = que.front();
que.pop
int x = p.first, y = p.second;
// printf("%d %d\n", x, y);
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx >= 0 && nx < H && ny >= 0 && ny < W && field[nx][ny] == '.'){
que.push(P(nx, ny));
field[nx][ny] = '#';
res++;
}
}
}
}
int main()
{
while(scanf("%d%d", &W, &H) && W){
res = 0;
for(int i = 0; i < H; i++)
scanf("%s", field[i]);
bfs();
printf("%d\n", res);
}
return 0;
}
2. AOJ 0118: Property Distribution
#include
int W, H;
const int MAXW = 105;
char field[MAXW][MAXW];
int res = 0;
int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};
void dfs(int x, int y)
{
char tmp = field[x][y];
field[x][y] = ' ';
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx >= 0 && nx < H && ny >= 0 && ny < W && field[nx][ny] == tmp){
dfs(nx, ny);
}
}
}
int main()
{
while(scanf("%d%d", &H, &W) && H){
res = 0;
for(int i = 0; i < H; i++)
scanf("%s", field[i]);
for(int i = 0; i < H; i++){
for(int j = 0; j < W; j++){
if(field[i][j] != ' '){
dfs(i, j);
res++;
}
}
}
printf("%d\n", res);
}
}
3. AOJ 0033: Ball
(1)法一:dfs(复杂度较高,是指数级别)
思路就是比完左边比右边,遍历所有情况。
#include
const int INF = -1e8;
int N;
int a[15];
int dfs(int x, int y, int i)
{
if(i == 9){
return (a[i] > x || a[i] > y);
}
if(a[i] > x && dfs(a[i], y, i + 1)){
return true;
}
if(a[i] > y && dfs(x, a[i], i + 1)){
return true;
}
return false;
}
int main()
{
scanf("%d", &N);
while(N--){
for(int i = 0; i < 10; i++){
scanf("%d", &a[i]);
}
if(dfs(INF, INF, 0))
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
(2)法二:贪心(复杂度是线性的)
思路是在保证递增的前提下,每次都把数组的值赋给与它的差较小的数。
#include
int N, a[15];
void solve()
{
int x = a[0], y = 0;
for(int i = 1; i < 10; i++){
if(a[i] > x && a[i] > y){
if(a[i] - x >= a[i] - y)
y = a[i];
else
x = a[i];
}
else if(a[i] > x)
x = a[i];
else if(a[i] > y)
y = a[i];
else{
printf("NO\n");
return;
}
}
printf("YES\n");
}
int main()
{
scanf("%d", &N);
while(N--){
for(int i = 0; i < 10; i++)
scanf("%d", &a[i]);
solve();
}
return 0;
}
4.POJ 3009: Curling 2.0
#include
#include
#include
using namespace std;
int W, H;
const int maxn = 25, INF = 1e8;
int sx, sy, gx, gy;
int field[maxn][maxn];
int dx[] = {0, -1, 0, 1}, dy[] = {1, 0, -1, 0};
int res, res0;
void dfs(int x, int y)
{
if(res > 10)
return;
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
int go = 0; //表示可以继续往此方向走。
while(nx >= 1 && nx <= H && ny >= 1 && ny <= W && field[nx][ny] != 1){
go = 1;
/*
这样子,若冰壶在上一次的滑行后,该方向并没有遇到边界或是墙(注:上一次碰的墙消失了),则会进入这个while循环,go的值变成1;表示该方向可继续前行。
需要强调的是,根据题意,若前方紧挨着墙,是不可以把墙撞碎的,即不可以往这个方向走。
*/
if(nx == gx && ny == gy){
res0 = min(res, res0);
}
/*
可以这么考虑,假如第一次抛冰壶就到终点,此时假如res初始值设为0,则输出就是0,这样不对,因此初始值设为1。
毕竟是在抛一次的过程中走一小步判断一次,而res是在抛完假设没到终点的情况下才加1。就算到了终点,按照代码的意思,冰壶也不会停下,
但这其实并不影响结果,要么大于十次后停止,要么再次回到终点,然后min函数把这个值给舍掉。
*/
nx += dx[i]; ny += dy[i];
}
// if(nx < 0 || nx >= H || ny < 0 || ny >= W){
// go = 0;
// return;
// }
if(field[nx][ny] == 1 && go){
field[nx][ny] = 0;
res++;
dfs(nx - dx[i], ny - dy[i]);
res--;
field[nx][ny] = 1;
/*
这两步很巧妙,虽然我们需要尝试很多种情况,每一种情况都需要对全局field和res作出改变,但是,我们利用递归,在每一次递归的后面我们
把刚才改变的值再变回来,这样既不影响每一种情况深度优先搜索,又很好的维护了全区变量在每一次递归前的初始值。
*/
}
}
}
int main()
{
while(scanf("%d%d", &W, &H) && W){
memset(field, 0, sizeof(field));
res = 1; res0 = INF;
/*
上面会解释为什么res初始值设为1
*/
/*
重要说明!!!
为什么数组从下标为1开始读入而不是0呢?假如从0开始读入,跑出界时,nx, ny会出现小于0的情况,
此时field[nx][ny] == 1这一判断时会越界;虽然不知为什么我的编译器没有出问题,但是这样还是错的。
*/
for(int i = 1; i <= H; i++){
for(int j = 1; j <= W; j++){
scanf("%d", &field[i][j]);
if(field[i][j] == 2){
sx = i; sy = j;
}
else if(field[i][j] == 3){
gx = i; gy = j;
}
}
}
dfs(sx, sy);
if(res0 > 10)
printf("%d\n", -1);
else
printf("%d\n", res0);
}
return 0;
}
1.AOJ 0558: Cheese
#include
#include
#include
using namespace std;
const int maxh = 1005, INF = 1e8, maxn = 14;
int H, W, N;
char field[maxh][maxh];
int dp[maxh][maxh], res = 0;
int sx, sy, gx, gy;
int dx[] = {1, 0, -1, 0}, dy[] = {0, -1, 0, 1};
typedef pair P;
P cheese[maxn];
int bfs()
{
for(int i = 0; i < H; i++){
for(int j = 0; j < W; j++){
dp[i][j] = INF;
}
}
queue que;
que.push(P(sx, sy));
dp[sx][sy] = 0;
while(que.size()){
P p = que.front();
que.pop();
int x = p.first, y = p.second;
// printf("%d %d\n", x, y);
if(x == gx && y == gy)
break;
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx >= 0 && nx < H && ny >= 0 && ny < W && field[nx][ny] != 'X' && dp[nx][ny] == INF){
que.push(P(nx, ny));
dp[nx][ny] = dp[x][y] + 1;
}
}
}
return dp[gx][gy];
}
int main()
{
scanf("%d%d%d", &H, &W, &N);
for(int i = 0; i < H; i++)
scanf("%s", field[i]);
for(int i = 0; i < H; i++){
for(int j = 0; j < W; j++){
if(field[i][j] >= '1' && field[i][j] <= '9'){
cheese[field[i][j] - '0'].first = i;
cheese[field[i][j] - '0'].second =j;
}
else if(field[i][j] == 'S'){
sx = i; sy = j;
}
}
}
for(int i = 1; i <= N; i++){
gx = cheese[i].first, gy = cheese[i].second;
res += bfs();
sx = gx; sy = gy;
}
printf("%d\n", res);
return 0;
}
(1)一定要注意!gx, gy这种全局变量一定要注意!不要在后面用的时候不要再int声明,不然会屏蔽掉外部变量!
(2)此题奶酪工厂是可以直接经过的,不是没吃过的工厂就就不让过。
2.POJ 3669: Meteor Shower
#include
#include
#include
using namespace std;
typedef pair P;
const int maxm = 50005, INF = 1e8, maxx = 305, maxt = 1005;
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
int field[maxx][maxx], N, dp[maxx][maxx];
vector m[maxt];
int bfs()
{
for(int i = 0; i < maxx; i++)
for(int j = 0; j < maxx; j++)
dp[i][j] = INF;
int sz = m[0].size();
//-1是砸过了,1是还没砸但将要砸,0是永远不会砸。
for(int j = 0; j < sz; j++){
int x = m[0][j].first, y = m[0][j].second;
field[x][y] = -1;
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx >= 0 && nx < maxx && ny >= 0 && ny < maxx)
field[nx][ny] = -1;
}
}
queue
que;
que.push(P(0, 0));
dp[0][0] = 0;
if(field[0][0] == -1){
return -1;
}
while(que.size()){
P p = que.front();
que.pop();
int x = p.first, y = p.second;
int t = dp[x][y] + 1;
int sz = m[t].size();
for(int j = 0; j < sz; j++){
int x1 = m[t][j].first, y1 = m[t][j].second;
// printf("%d %d\n", x1, y1);
for(int i = 0; i < 4; i++){
int nx = x1 + dx[i], ny = y1 + dy[i];
if(nx >= 0 && nx < maxx && ny >= 0 && ny < maxx)
field[nx][ny] = -1;
}
}
if(field[x][y] == 0)
return dp[x][y];
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx >= 0 && nx < maxx && ny >= 0 && ny < maxx && field[nx][ny] != -1 && dp[nx][ny] == INF){
que.push(P(nx, ny));
dp[nx][ny] = dp[x][y] + 1;
}
}
}
return -1;
}
int main()
{
int mx, my, mt;
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d%d%d", &mx, &my, &mt);
m[mt].push_back(P(mx, my));
field[mx][my] = 1;
for(int i = 0; i < 4; i++){
int nx = mx + dx[i], ny = my + dy[i];
if(nx >= 0 && nx < maxx && ny >= 0 && ny < maxx)
field[nx][ny] = 1;
}
}
int res = bfs();
printf("%d\n", res);
return 0;
}
(1)注:一定是先砸,再判断怎么走;而且,砸的时间是dp[x][y] + 1,因为要判断到(x,y)之后的那一秒流星是怎么砸的。
(2)调试的时候,可以用printf()输出地图,看看输入是否正确;可以输出dp数组,看看程序大致的运行情况;可以再关键的地方输出类似"I'm here\n"这种与程序无关的话,看看程序是否运行到这一步;可以再调试打印的地方设置断点,让自己知道这里插入了调试的语句,方便提交的时候找到他们并把它们注释掉,虽说不是这样用的,但是这样子方便。
3.AOJ 0121: Seven Puzzle
#include
//#include
#include
#include
#include
(1)C++ string类(C++字符串)完全攻略
(2)string类可以直接用'='赋初值,且用字符或字符串赋初值均可。
(3)在声明string的时候,只能用字符串赋初值。
(4)string可以索引;想在string后面添加字符或字符串时,直接用 '+=' 即可。
(5)string的输出要用cout,不能用pringf。
1.POJ 2718: Smallest Difference
法一:用int数组储存输入的值,较为麻烦,这里的代码用了枚举集合的方法,但是其实这个方法不需要枚举集合。虽然复杂度比较高,但POJ还是通过了。
#include
#include
#include
#include
#include
#include
using namespace std;
int a[15], N, res = 1e9;
vector c, b;
int k;
void solve()
{
if(k == 2){
printf("%d\n", abs(a[0] - a[1])); //避免后边判断首位是否为0
return;
}
for(int i = 1; i < (1 << k) - 1; i++){
b.clear(), c.clear();
for(int j = 0; j < k; j++){
if((i >> j) & 1){
b.push_back(a[j]);
}
else
c.push_back(a[j]);
}
int sz1 = b.size(), sz2 = c.size();
if(abs(sz1 - sz2) > 1)
continue;
int b2[sz1], c2[sz2];
for(int k = 0; k < sz1; k++)
b2[k] = b[k];
for(int k = 0; k < sz2; k++)
c2[k] = c[k];
sort(b2, b2 + sz1);
sort(c2, c2 + sz2);
do{
do{
int sum1 = 0, sum2 = 0;
if(b2[0] == 0 || c2[0] == 0)
continue;
for(int i = 0; i < sz1; i++)
sum1 = sum1 * 10 + b2[i];
for(int i = 0; i < sz2; i++)
sum2 = sum2 * 10 + c2[i];
res = min(res, abs(sum1 - sum2));
}while(next_permutation(b2, b2 + sz1));
}while(next_permutation(c2, c2 + sz2));
}
printf("%d\n", res);
}
int main()
{
int c;
scanf("%d", &N);
getchar();
for(int i = 0; i < N; i++){
res = 1e9;
memset(a, -1, sizeof(a));
k = 0;
while(c = getchar()){
if(c == '\n' || c == '\r')
break;
if(isdigit(c))
a[k++] = c - '0';
}
solve();
}
return 0;
}
法二:将输入的结果保存在字符串中,操作也是基于字符串。
#include
#include
#include
#include
using namespace std;
char a[15];
int N, res = 1e9;
int k;
void solve()
{
if(k == 2){
printf("%d\n", abs(a[0] - a[1])); //避免后面判断首位是否为0
return;
}
int mid = k / 2;
sort(a, a + k);
do{
if(a[0] == '0' || a[mid] == '0') //一定要注意,这里的0是字符0,千万别写成数字0!
continue;
int sum1 = 0, sum2 = 0;
for(int i = 0; i < mid; i++)
sum1 = sum1 * 10 + (a[i] - '0');
for(int i = mid; i < k; i++)
sum2 = sum2 * 10 + (a[i] - '0');
res = min(res, abs(sum1 - sum2));
}while(next_permutation(a, a + k));
printf("%d\n", res);
}
int main()
{
int c;
scanf("%d", &N);
getchar();
for(int i = 0; i < N; i++){
res = 1e9;
k = 0;
while(c = getchar()){
if(c == '\n' || c == '\r')
break;
if(isdigit(c))
a[k++] = c;
}
solve();
}
return 0;
}
(1)一定要注意,要是想枚举全排列,在next_permutation(a, a + k)之前一点更要先排序
(2)sort也可以对字符串排序,但切记字符串首尾是否为'0'一定要判断字符‘0’,而不是数字零0。
(3)next_permutation是枚举全排列,但只是内部原理有点奇怪,一定要先排序;
2.POJ 3187: Backward Digit Sums
#include
#include
using namespace std;
const int maxn = 15;
int N, sum, a[maxn];
bool check()
{
int res[2][maxn], n = N;
for(int i = 1; i <= N; i++)
res[N & 1][i] = a[i];
while(--n){
for(int i = 1; i <= n; i++)
res[n & 1][i] = res[(n + 1) & 1][i] + res[(n + 1) & 1][i + 1];
}
if(res[1][1] == sum){
return true;
}
return false;
}
void solve()
{
for(int i = 1; i <= N; i++){
a[i] = i;
}
do{
if(check()){
for(int i = 1; i < N; i++)
printf("%d ", a[i]);
printf("%d\n", a[N]);
return;
}
}while(next_permutation(a + 1, a + N + 1));
}
int main()
{
scanf("%d%d", &N, &sum);
solve();
return 0;
}
注:这道题比较简单,但是需要注意两点:一是注意二维数组滚动使用,变化的还有n,因此可以用n & 1来确定数组滚动到了那一维。二是注意最终结果都是在res[1][1]中,和N的奇偶性无关。
3.POJ 3050: Hopscotch
#include
#include
#include
using namespace std;
int field[10][10];
const int maxn = 1e6;
set six;
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
void dfs(int x, int y, int k, int sum)
{
if(k == 6){
six.insert(sum);
return;
}
k++;
for(int i = 0; i < 4; i++){
int now = sum;
int nx = x + dx[i], ny = y + dy[i];
if(nx >= 0 && nx < 5 && ny >= 0 && ny < 5){
now = now * 10 + field[nx][ny];
dfs(nx, ny, k, now);
}
}
}
int main()
{
for(int i = 0; i < 5; i++){
for(int j = 0; j < 5; j++){
scanf("%d", &field[i][j]);
}
}
for(int i = 0; i < 5; i++){
for(int j = 0; j < 5; j++){
dfs(i, j, 1, field[i][j]);
}
}
int res = six.size();
printf("%d\n", res);
return 0;
}
(1)大胆地用set,尤其是去重的时候,因为set里面很好地实现了平衡二叉树,是非常快的!
(2)另外提供一个超时的做法,目前原因并不算清楚,应该去读读这三门书《数据结构》,《cpp primer》,《cpp primer plus》;
4.AOJ 0525: Osenbei
#include
#include
using namespace std;
bool field[15][10005];
int r, c;
int maxium = 0;
int sumup()
{
//按照列来统计,只取统计一列中正面或背面朝上更多的,相当于把这一列反面
int sum = 0, cnt;
for(int j = 0; j < c; j++){
cnt = 0;
for(int i = 0; i < r; i++){
cnt += field[i][j];
}
sum += max(cnt, r - cnt);
}
return sum;
}
void rev(int n)
{
for(int j = 0; j < c; j++){
field[n][j] = !field[n][j];
}
}
int dfs(int n)
{
if(n == r)
return maxium = max(maxium, sumup());
rev(n);
dfs(n + 1);
rev(n);
dfs(n + 1);
}
int main()
{
while(scanf("%d%d", &r, &c) == 2 && r){
for(int i = 0; i < r; i++){
for(int j = 0; j < c; j++){
scanf("%d", &field[i][j]);
}
}
int res = dfs(0);
printf("%d\n", res);
}
return 0;
}
(1)我一开始的思路是设置无限循环,每一次循环都先按行遍历,再按列遍历,如果发现正面小于反面,就翻面。直到某一次循环一次也没有翻面就结束,但这样是不对的。这道题是要遍历所有翻面的情况。
(2)这次用的dfs,因为行数不超过10,因此dfs用行数。至于每一列怎么反转,代码中有描述。而且,每一行与每一列的翻面是相对独立的,因为某一列与某一行翻转顺序不影响最终翻转结果。
1.POJ 3617: Best Cow Line
#include
#include
using namespace std;
const int MAXN = 2005;
char S[MAXN], T[MAXN];
int N, sz;
void solve()
{
int a = 0, b = N - 1;
bool left = false;
while(a <= b){
for(int i = 0; a + i <= b - i; i++){
//这里写成 <= b也可以,因为若 a + i > b - i 还无法判断的话,一定是回文串,那么加上哪个都一样。
if(S[a + i] > S[b - i]){
left = false;
break;
}
else if(S[a + i] < S[b - i]){
left = true;
break;
}
}
if(left)
T[sz++] = S[a++];
else
T[sz++] = S[b--];
}
for(int i = 0; i < sz; i++){
printf("%c", T[i]);
if((i + 1) % 80 == 0 || (i + 1) == sz){
printf("\n");
}
}
}
int main()
{
scanf("%d", &N);
getchar();
char p;
for(int i = 0; i < N; i++){
scanf("%c", &S[i]);
getchar();
}
// printf("%s\n", S);
solve();
return 0;
}
2.POJ 3069:Saruman's Army
#include
#include
using namespace std;
const int MAXN = 1005;
int N, R, A[MAXN], res;
void solve()
{
sort(A, A + N);
int bound = A[0], heart;
int m = 0;
while(m < N){
while(A[m] <= bound + R && m < N){
m++;
}
heart = A[m - 1];
res++;
while(A[m] <= heart + R && m < N){
m++;
}
bound = A[m];
}
printf("%d\n", res);
}
int main()
{
while(scanf("%d%d", &R, &N) == 2 && R != -1){
res = 0;
for(int i = 0; i < N; i++){
scanf("%d", &A[i]);
}
solve();
}
}
3.POJ 3253:Fence Repair
#include
#include
using namespace std;
int N, A[20005];
typedef long long ll;
ll res;
void solve()
{
int n = N;
while(n > 2){
int min1 = A[0], min2 = A[1], idx1 = 0, idx2 = 1;
if(min1 > min2){
swap(min1, min2);
swap(idx1, idx2);
}
for(int i = 2; i < n; i++){
if(A[i] < min2){
if(A[i] < min1){
min2 = min1;
min1 = A[i];
idx2 = idx1;
idx1 = i;
}
else{
min2 = A[i];
idx2 = i;
}
}
}
res += (ll)min1 + (ll)min2;
// printf("%d %d %d %d %d\n", idx1, idx2, min1, min2, res);
A[idx1] = min1 + min2;
swap(A[idx2], A[n-1]);
n--;
}
res += (ll)A[0] + (ll)A[1];
printf("%lld\n", res);
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d", &A[i]);
}
solve();
return 0;
}
(1)说一件事,就是答案是long long类型的,要有判断意识。
(2)不用保存最大最小值,只要保存其坐标即可。对程序稍作修改,把涉及改动min1,min2值的部分改掉,把适当位置的min1改为idx1,min2改为idx2即可。
4.POJ 2376: Cleaning Shifts
#include
#include
using namespace std;
int N, T, res = 1;
const int MAXN = 25005;
int S[MAXN], E[MAXN];
struct P
{
int s;
int t;
};
bool com(P a, P b)
{
if(a.s != b.s) return a.s < b.s;
else return a.t >= b.t;
}
P G[MAXN];
void solve()
{
for(int i = 0; i < N; i++){
G[i].s = S[i];
G[i].t = E[i];
}
sort(G, G + N, com);
if(G[0].s != 1){
printf("%d\n", -1);
return;
}
if(N == 1){
if(G[0].t != T){
printf("%d\n", -1);
return;
}
else{
printf("%d\n", res);
return;
}
}
int i = 1, maxT = G[0].t, maxNextT = G[0].t;
while(i < N){
if(maxT == T){ //这样一旦符合条件可以提前退出循环,其实这一行判断放在while循环的末尾也是对的,可以AC;
printf("%d\n", res);
return;
}
if(G[i].s <= maxT + 1){
int flag = 0;
while(i < N && G[i].s <= maxT + 1){
if(G[i].t > maxNextT){
maxNextT = G[i].t;
flag = 1;
}
i++;
}
if(flag){
maxT = maxNextT;
res++;
}
}
else{
printf("%d\n", -1);
return;
}
}
if(maxT == T){ //在这里判断,若循环到最后也没能找到结束时间为T的牛,则输出-1;
printf("%d\n", res);
return;
}
else{
printf("%d\n", -1);
return;
}
}
int main()
{
scanf("%d%d", &N, &T);
for(int i = 0; i < N; i++){
scanf("%d%d", &S[i], &E[i]);
}
solve();
return 0;
}
(1)自定义结构,让相同的s值的不同元素中,第一个元素的e值尽可能大
(2)每次保存的是maxnexte而不是保存下标,这样可以让i的值一直增大,而不用考虑其他的,真的很简便
(3)我感觉一个好的思路是不应该处理过多的边界条件的,否则一定有问题。
5.POJ 1328: Radar Installation
两种方法,思路是一样的,处理起来略有不同。反正可容易错。
法一:
#include
#include
#include
using namespace std;
const int MAXN = 1005;
int N, X[MAXN], Y[MAXN], D, res, kase;
struct P
{
double first, second;
};
bool com(P a, P b)
{
if(a.first != b.first)
return a.first < b.first;
else
return a.second <= b.second;
}
P G[MAXN];
void solve()
{
double x1, x2;
double x;
for(int i = 0; i < N; i++){
if(Y[i] > D || Y[i] < -D || D < 0){
printf("Case %d: %d\n", ++kase, -1);
return;
}
x = sqrt((double)(D * D) - (double)(Y[i] * Y[i]));
x1 = (double)X[i] - x;
x2 = (double)X[i] + x;
G[i] = P{x1, x2};
}
sort(G, G + N, com);
int i = 0;
// for(int j = 0; j < N; j++){
// printf("%lld %lld\n", G[j].first, G[j].second);
// }
while(i < N){
if(N == 1){
res++;
break;
}
// printf("Im here\n");
res++;
if(i < N && G[i].second >= G[i + 1].first){
while(i < N - 1 && G[i].second >= G[i + 1].first){
if(G[i].second < G[i + 1].second)
G[i + 1].second = G[i].second;
i++;
}
i++;
}
else{
i++;
}
}
printf("Case %d: %d\n", ++kase, res);
}
int main()
{
while(scanf("%d%d", &N, &D) == 2 && N){
res = 0;
for(int i = 0; i < N; i++){
scanf("%d%d", &X[i], &Y[i]);
}
solve();
}
return 0;
}
法二:
#include
#include
#include
using namespace std;
const int MAXN = 1005;
int N, X[MAXN], Y[MAXN], D, res, kase;
struct P
{
double first, second;
};
bool com(P a, P b)
{
if(a.first != b.first)
return a.first < b.first;
else
return a.second <= b.second;
}
P G[MAXN];
void solve()
{
double x1, x2;
double x;
for(int i = 0; i < N; i++){
if(Y[i] > D || Y[i] < -D || D < 0){
printf("Case %d: %d\n", ++kase, -1);
return;
}
x = sqrt(D * D - Y[i] * Y[i]);
x1 = (double)X[i] - x;
x2 = (double)X[i] + x;
G[i] = P{x1, x2};
}
sort(G, G + N, com);
for (int i = 0; i < N; i++) {
res++;
double xx = G[i].second;
for (int j = i+1; j < N; j++) {
if (G[j].first<=xx) {
if (G[j].second < xx)
xx = G[j].second;
i = i+1;
}
else {
break;
}
}
}
printf("Case %d: %d\n", ++kase, res);
}
int main()
{
while(scanf("%d%d", &N, &D) == 2 && N){
res = 0;
for(int i = 0; i < N; i++){
scanf("%d%d", &X[i], &Y[i]);
}
solve();
}
return 0;
}
(1)若 D <= 0,其实也不影响,Y[i] > D,这一步也可以判断出。
(2)此题与那个略有区别,包含四种情况:s处包含G[i]但G[i]不包含s,s处不包含G[i]但G[i]包含s,s处包含G[i]且G[i]不包含s,s处不包含G[i]且G[i]不包含s。但是,p处和G[i]处可类似分析,有两种情况。
(3)floor(x)返回的是小于或等于x的最大整数。ceil(x)返回的是大于或等于x的最小整数。但是这题的雷达坐标不一定是整数啦啊。
6.POJ 3190: Stall Reservations
不知道哪一步比较慢,但就是一直超时,我感觉应该是奶牛多的话,循环的次数有点多。
#include
#include
using namespace std;
const int MAXN = 50005;
int N, S[MAXN], T[MAXN], cow[MAXN], res;
struct Node
{
int t, s, idx;
};
bool com(Node a, Node b)
{
if(a.t != b.t)
return a.t < b.t;
else
return a.s >= b.s;
}
Node G[MAXN];
void solve()
{
sort(G, G + N, com);
int k = N;
while(k){
res++;
Node tmp = G[0];
int tmpidx = 0;
for(int i = 0; i < k; i++){
if(G[i].s > tmp.t){
cow[tmp.idx] = res;
G[tmpidx].s = -1;
tmp = G[i];
tmpidx = i;
}
}
cow[tmp.idx] = res;
G[tmpidx].s = -1;
int sz = 0;
for(int i = 0; i < k; i++){
if(G[i].s != -1){
G[sz++] = G[i];
}
}
k = sz;
}
printf("%d\n", res);
for(int i = 0; i < N; i++){
printf("%d\n", cow[i]);
}
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d%d", &G[i].s, &G[i].t);
G[i].idx = i;
}
solve();
return 0;
}
法二:
#include
#include
#include
using namespace std;
const int MAXN = 50005;
int N, S[MAXN], T[MAXN], cow[MAXN];
struct Node
{
int t, s, idx;
friend bool operator > (Node a, Node b)
{
return a.t > b.t;
}
};
bool com(Node a, Node b)
{
if(a.s == b.s)
return a.t < b.t;
else
return a.s < b.s;
}
Node G[MAXN];
priority_queue, greater > que;
void solve()
{
sort(G, G + N, com);
que.push(G[0]);
cow[G[0].idx] = 1;
for(int i = 1; i < N; i++){
if(G[i].s > que.top().t){
cow[G[i].idx] = cow[que.top().idx];
que.pop();
que.push(G[i]);
}
else{
que.push(G[i]);
cow[G[i].idx] = que.size();
}
}
printf("%d\n", que.size());
for(int i = 0; i < N; i++){
printf("%d\n", cow[i]);
}
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d%d", &G[i].s, &G[i].t);
G[i].idx = i;
}
solve();
return 0;
}
(1)还可以用优先队列做,先按照起始时间从小到大排序,然后优先队列按照结束时间最早的优先级最高。这样,每加入一个元素,就可以和尚未结束的奶牛中结束时间最早的奶牛对比,若起始时间比他结束时间早,就再开一个牛棚,如果晚的话就取代掉。
(2) 注意了,如果自定义结构的话,如果排序的话,要定义com函数。但是,要定义有此结构定义的队列的话,要重载<运算符,要是定义优先队列的话,要重载>运算符。
7.POJ 2393: Yogurt factory
#include
#include
using namespace std;
const int MAXN = 10005;
int N, S, C[MAXN], Y[MAXN];
typedef long long ll;
ll res;
void solve()
{
for(int i = 0; i < N; i++){
if(C[i] != -1){
res += (ll)C[i] * Y[i];
for(int j = i + 1; j < N; j++){
if(C[j] < C[i] + S * (j - i)){
break;
}
else{
res +=(ll)Y[j] * (C[i] + S * (j - i));
C[j] = -1;
}
}
}
}
printf("%lld\n", res);
}
int main()
{
scanf("%d%d", &N, &S);
for(int i = 0; i < N; i++){
scanf("%d%d", &C[i], &Y[i]);
}
solve();
return 0;
}
注:很水的一道题,但是还是得注意结果数字!可能为long long,输出的时候要用%lld,这个要有意识。
8.POJ 1017: Packets
#include
int n1, n2, n3, n4, n5, n6, res;
void solve()
{
res += n6 + n5 + n4 + (n3 + 3) / 4;
//n5, n6填箱子的时候剩余的空间n2填不进去,则先算需要多少n2的箱子去填充n4
int left2 = n4 * 5;
//计算n3填完后剩余空间最多可以塞几个n2箱子。
if(n3 % 4 != 0){
int m = 4 - n3 % 4;
left2 += m * 2 - 1;
}
if(left2 < n2){
res += (n2 - left2 + 8) / 9;
//n2仍然多于最大的能塞的箱子数,那就再开启新的箱子。
}
//计算最多还能塞下多少个n1的箱子
int left1 = res * 36 - n2 * 4 - n3 * 9 - n4 * 16 - n5 * 25 - n6 * 36;
if(left1 < n1){
res += (n1 - left1 + 35) / 36;
}
printf("%d\n", res);
}
int main()
{
while(scanf("%d%d%d%d%d%d", &n1, &n2, &n3, &n4, &n5, &n6) && (n1 || n2 || n3 || n4 || n5 || n6)){
res = 0;
solve();
}
return 0;
}
(1)先填r3~r6,然后算出剩余空间需要多少r2去填充,最后相减得到所需r1填充的个数。
9.POJ 3040: Allowance
#include
#include
#include
using namespace std;
typedef pair P;
P G[25];
int N, C, res , INF = 1e9;
int coin_used[25];
bool com(P a, P b)
{
return a.first > b.first;
}
void solve()
{
sort(G, G + N, com);
for(int i = 0; i < N; i++){
if(G[i].first >= C){
res += G[i].second;
G[i].second = 0;
}
else
break;
}
while(1){
memset(coin_used, 0, sizeof(coin_used));
int flag = 0, cost = C;
for(int i = 0; i < N; i++){
if(G[i].second){
coin_used[i] = min(cost / G[i].first, G[i].second);
cost -= coin_used[i] * G[i].first;
if(cost == 0){
flag = 1;
break;
}
}
}
if(cost > 0){
for(int i = N - 1; i >= 0; i--){
if(G[i].second > coin_used[i]){
while(G[i].second > coin_used[i]){
cost -= G[i].first;
coin_used[i]++;
if(cost <= 0){
flag = 1;
break;
}
}
if(cost <= 0){
break;
}
}
}
}
if(!flag){
break;
}
int maxn = INF;
for(int i = 0; i < N; i++){
if(coin_used[i]){
maxn = min(G[i].second / coin_used[i], maxn); //计算当前取硬币的方案最多可以发几周
}
}
res += maxn;
for(int i = 0; i < N; i++){
if(coin_used[i]){
G[i].second -= coin_used[i] * maxn;
}
}
}
printf("%d\n", res);
}
int main()
{
scanf("%d%d", &N, &C);
for(int i = 0; i < N; i++){
scanf("%d%d", &G[i].first, &G[i].second);
}
solve();
return 0;
}
(1)找出面额不小于C的硬币,直接加到res上面;
(2)接着开始硬币从大到小筛选,一直加到等于C为止,或是不超过C;
(3)若可以正好等于C的话,跳到第(5)步;
(4)若是小于C,把硬币按面额从小到大开始填,一个一个硬币往上加,直到大于等于C,或是该面额硬币用完。
(5)判断,如果(3)(4)始终无法加到大于等于C的话,这表明剩余的钱已不足以支付一周的工资,直接跳到最后一步。
(6)因为(2)~(4)寻找的是一种方案,接着可以看看这种方案最多可以发几周的工资,加到res上,然后在硬币总计中减去相应硬币数。返回第二步。
(7)输出res。
10.POJ 1862: Stripies
C++和C语言一样,输出都是%.f。
#include
#include
#include
using namespace std;
double a[105];
int N;
void solve()
{
if(N == 1){
printf("%.3f\n", a[0]);
return;
}
while(N > 1){
int max1 = 0, max2 = 1;
if(a[max1] < a[max2]){
swap(max1, max2);
}
for(int i = 2; i < N; i++){
if(a[i] > a[max1]){
max2 = max1;
max1 = i;
}
else if(a[i] > a[max2]){
max2 = i;
}
}
a[max1] = 2.0 * sqrt(a[max1] * a[max2]);
swap(a[max2], a[N - 1]);
N--;
}
printf("%.3f\n", a[0]);
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%lf", &a[i]);
}
solve();
return 0;
}
不过,还是要注意最后那个把一个数扔到N - 1那个位置的操作,又错了。
法二:这和割木板的不同,最大的两个数字合并后还是最大的啊。
#include
#include
#include
using namespace std;
double a[105];
int N;
void solve()
{
if(N == 1){
printf("%.3f\n", a[0]);
return;
}
sort(a, a + N);
for(int i = N - 1; i > 0; i--){
a[i - 1] = 2 * sqrt(a[i - 1] * a[i]);
}
printf("%.3f\n", a[0]);
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%lf", &a[i]);
}
solve();
return 0;
}
证明:
假设有a,b,c 且结果是r
则 r = 2*sqrt(2*sqrt(a*b)*c)
则 r^2/8 = sqrt(a*b*c*c);
若让a*b*c*c最小,即(a*b*c)*c最小,因为(a*b*c)不管怎样是不变的,因此c一定是最小的,因此不断去取两个较大的数相乘。
11.POJ 3262: Protecting the Flowers
(1)这个解法超时了
#include
#include
using namespace std;
const int MAXN = 100005;
int T[MAXN], D[MAXN], N;
typedef long long ll;
ll W, res;
void solve()
{
while(N > 0){
ll minw = (ll)(W - D[0]) * T[0];
int idx = 0;
for(int i = 1; i < N; i++){
ll tmp = (ll)(W - D[i]) * T[i];
if(minw > tmp){
idx = i;
minw = tmp;
}
}
res += 2 * minw;
W -= D[idx];
swap(D[N - 1], D[idx]);
swap(T[N - 1], T[idx]);
N--;
}
printf("%lld\n", res);
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d%d", &T[i], &D[i]);
W += D[i];
}
solve();
return 0;
}
(2)实际上,上面的那个方法是对的,但是需要改动,在N头牛中,任选两头牛a, b,无论先拉a还是先拉b,其他牛吃花的数量是确定的,因此,排序的规则应该为a.t * b.d < a.d * b.t;
#include
#include
using namespace std;
const int MAXN = 100005;
int N;
typedef long long ll;
ll res, W;
struct Node
{
int t, d;
friend bool operator < (Node a, Node b)
{
return a.t * b.d < a.d * b.t;
}
};
Node G[MAXN];
void solve()
{
sort(G, G + N);
for(int i = 0; i < N; i++){
W -= (ll)G[i].d;
res += 2 * W * (ll)G[i].t;
}
printf("%lld\n", res);
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d%d", &G[i].t, &G[i].d);
W += (ll)G[i].d;
}
solve();
return 0;
}
(3)法三,更厉害(其实通过对法二的a.t * b.d < a.d * b.t变一下型就是法三了):
任意一头牛,例如:
2 7
可以看成两头 1 3.5的牛
也就是说任意一头牛都可以拆成T头
1 D/T
的牛
所以说,所有的牛就都变成了
1 X
#include
#include
using namespace std;
const int MAXN = 100005;
int N;
typedef long long ll;
ll res, W;
struct Node
{
int t, d;
double x;
friend bool operator < (Node a, Node b)
{
return a.x > b.x;
}
};
Node G[MAXN];
void solve()
{
sort(G, G + N);
for(int i = 0; i < N; i++){
W -= (ll)G[i].d;
res += 2 * W * (ll)G[i].t;
}
printf("%lld\n", res);
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d%d", &G[i].t, &G[i].d);
G[i].x = (double)G[i].d / (double)G[i].t;
W += (ll)G[i].d;
}
solve();
return 0;
}
1.最长上升子序列
#include
#include
using namespace std;
int N, a[1005], dp[1005];
void solve()
{
for(int i = 0; i < N; i++){
dp[i] = 1;
}
for(int i = 0; i < N; i++){
for(int j = 0; j < i; j++){
if(a[j] < a[i]){
dp[i] = max(dp[i], dp[j] + 1);
}
else{
dp[i] = max(dp[i], dp[i - 1]);
}
}
}
printf("%d", dp[N - 1]);
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d", &a[i]);
}
solve();
return 0;
}
(1)我的代码也不知道对不对,感觉是对的。
(2)求一个排好序的数组的指定元素出现次数:
upperbound(a, a + N, k) - lowerbound(a, a + N, k);
不行不行,书上的这个代码写得太好了,忍不住搬了过来。
#include
#include
using namespace std;
const int INF = 1e8;
int N, a[1005], dp[1005];
void solve()
{
fill(dp, dp + N, INF);
for(int i = 0; i < N; i++){
*lower_bound(dp, dp + N, a[i]) = a[i];
}
printf("%d\n", lower_bound(dp, dp + N, INF) - dp);
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d", &a[i]);
}
solve();
return 0;
}
2.POJ 3176: Cow Bowling
#include
#include
using namespace std;
int N, tri[355][355], dp[355][355];
void solve()
{
dp[0][0] = tri[0][0];
int tmp = 0;
for(int i = 1; i < N; i++){
for(int j = 0; j < i + 1; j++){
if(j == 0){
dp[i][j] = max(dp[i][j], dp[i - 1][0] + tri[i][j]);
}
else if(j == i){
dp[i][j] = max(dp[i][j], dp[i - 1][i - 1] + tri[i][j]);
}
else{
tmp = max(dp[i - 1][j - 1], dp[i - 1][j]);
dp[i][j] = max(dp[i][j], tmp + tri[i][j]);
}
}
}
int res = 0;
for(int j = 0; j < N; j++){
res = max(res, dp[N - 1][j]);
}
printf("%d\n", res);
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
for(int j = 0; j < i + 1; j++){
scanf("%d", &tri[i][j]);
}
}
solve();
return 0;
}
水题,但是刘汝佳是从下往上跑的,因此在最后一次就不用判断最大最小值了,一定是dp[0][0]。
3.POJ 2229: Sumsets
#include
int N, dp[1000005];
const int M = 1e9;
void solve()
{
dp[1] = 1;
dp[2] = 2;
for(int i = 3; i <= N; i++){
dp[i] = dp[i - 1];
i++;
dp[i] = (dp[i - 1] + dp[i / 2]) % M;
}
printf("%d\n", dp[N]);
}
int main()
{
scanf("%d", &N);
solve();
return 0;
}
(1)首先要清楚,当一组解中喊奇数时,只可能是1,不可能是其他奇数,而这可以转化为在dp[i-1]的每组解中都加上一个1。而且,一旦解中不含奇数时,即全是偶数,那么这种解的个数和dp[i /2]是一样的。
(2)i是奇数时,那么在每一组分解结果中不可能全是偶数。只有i是偶数时,他才可能会出现解全为偶数的情况。
4.POJ 2385: Apple Catching
#include
#include
using namespace std;
int T, W, t[1005], dp[35][1005];
void solve()
{
if(t[0] == 1){
dp[0][0] = 1;
//注意dp[0][0]的意义,第一个0表示一步也没有移动,第二个0表示第一个苹果已经落下。因为t是从0开始计数。
dp[1][0] = 0;
}
else{
dp[0][0] = 0;
dp[1][0] = 1;
}
for(int j = 1; j < T; j++){
dp[0][j] = dp[0][j - 1] + t[j] % 2;
}
for(int j = 1; j < T; j++){
for(int i = 1; i <= W; i++){
//新的苹果掉下来之前,把上一个状态最大苹果树传递过来。
dp[i][j] = max(dp[i][j - 1], dp[i - 1][j - 1]);
if(i % 2 + 1 == t[j]){
dp[i][j]++;
}
}
}
//最大值不一定是移动w次,可能不到w次。
int res = 0;
for(int i = 0; i <= W; i++){
res = max(dp[i][T - 1], res);
}
printf("%d\n", res);
}
int main()
{
scanf("%d%d", &T, &W);
for(int i = 0; i < T; i++){
scanf("%d", &t[i]);
}
solve();
return 0;
}
他们都说这道题经典,简单。我一点也不觉得简单。
5.POJ 3616: Milking Time
#include
#include
using namespace std;
int N, M, R;
struct P
{
int s, t, e;
friend bool operator < (P a, P b)
{
return a.t < b.t;
}
};
P G[1005];
int dp[1000005];
void solve()
{
sort(G, G + M);
int k = 0;
for(int i = 1; i <= N; i++){
dp[i] = max(dp[i], dp[i - 1]);
//寻找i时的最大值。
while(k < M && G[k].t <= i){
if(G[k].s - R < 0){
dp[i] = max(dp[i], G[k].e);
}
else{
dp[i] = max(dp[i], dp[G[k].s - R] + G[k].e);
}
k++;
}
}
int res = 0;
for(int i = 0; i <= N; i++){
res = max(res, dp[i]);
}
printf("%d\n", res);
}
int main()
{
scanf("%d%d%d", &N, &M, &R);
for(int i = 0; i < M; i++){
scanf("%d%d%d", &G[i].s, &G[i].t, &G[i].e);
}
solve();
return 0;
}
老实讲,我觉得这个题挺难的,这个代码我还是感觉自己很难设计出来。
#include
#include
using namespace std;
int N, M, R;
struct P
{
int s, t, e;
friend bool operator < (P a, P b)
{
if(a.s != b.s)
return a.s < b.s;
else
return a.t < b.t;
}
};
P G[1005];
int dp[1005];
void solve()
{
sort(G, G + M);
for(int i = 0; i < M; i++){
dp[i] = G[i].e;
}
for(int i = 0; i < M; i++){
for(int j = 0; j < i; j++){
if(G[j].t + R <= G[i].s){
dp[i] = max(dp[i], dp[j] + G[i].e);
}
}
}
int res = 0;
//注意了,这道题最大值可不一定是dp[M - 1],因为有一个if(G[j].t + R <= G[i].s)判断,最后一个挤奶时间不一定会被选择。
for(int i = 0; i < M; i++){
res = max(res, dp[i]);
}
printf("%d\n", res);
}
int main()
{
scanf("%d%d%d", &N, &M, &R);
for(int i = 0; i < M; i++){
scanf("%d%d%d", &G[i].s, &G[i].t, &G[i].e);
}
solve();
return 0;
}
6.POJ 3280: Cheapest Palindrome
这题啊,见一道不会一道,真是没办法。
#include
#include
using namespace std;
int N, M, dp[2005][2005], cost[30];
char str[2005];
void solve()
{
for(int i = M - 2; i >= 0; i--){
for(int j = i + 1; j < M; j++){
if(str[i] == str[j]){
dp[i][j] = dp[i + 1][j - 1];
}
else{
dp[i][j] = min(dp[i + 1][j] + cost[str[i] - 'a'], dp[i][j - 1] + cost[str[j] - 'a']);
}
}
}
printf("%d\n", dp[0][M - 1]);
}
int main()
{
scanf("%d%d", &N, &M);
getchar();
scanf("%s", str);
getchar();
char p;
int del, add;
for(int i = 0; i < N; i++){
p = getchar();
scanf("%d%d", &add, &del);
cost[p - 'a'] = min(add, del);
getchar();
}
solve();
return 0;
}
7.POJ 1742: Coins
#include
#include
#include
using namespace std;
int N, M, a[105], c[105], res;
int dp[100005];
void solve()
{
memset(dp, -1, sizeof(dp));
dp[0] = 0;
for(int i = 0; i < N; i++){
for(int j = 0; j <= M; j++){
if(dp[j] >= 0){
dp[j] = c[i];
}
else if(j < a[i] || dp[j - a[i]] <= 0){
dp[j] = -1;
}
else{
dp[j] = dp[j - a[i]] - 1;
}
}
}
for(int j = 1; j <= M; j++){
if(dp[j] >= 0){
res++;
}
}
printf("%d\n", res);
}
int main()
{
while(scanf("%d%d", &N, &M) == 2 && N){
res = 0;
for(int i = 0; i < N; i++){
scanf("%d", &a[i]);
}
for(int i = 0; i < N; i++){
scanf("%d", &c[i]);
}
solve();
}
return 0;
}
1900MS,很危险,用两个滚动数组就TIL了。看看排行榜,有的代码就200多毫秒,nb,得学学。
#include
#include
#include
using namespace std;
int N, M, a[105], c[105], res;
bool dp[100005];
int used[100005];
void solve()
{
dp[0] = true;
for(int i = 0; i < N; i++){
//这里为什么不设置一个int型变量对硬币计数呢?
//是因为,假如前面的数量是2和3,但手里的硬币是5,这样这一个硬币是可以凑两个数的。
memset(used, 0, sizeof(used));
for(int j = a[i]; j <= M; j++){
if(!dp[j] && dp[j - a[i]] && used[j - a[i]] < c[i]){
dp[j] = true;
used[j] = used[j - a[i]] + 1;
res++;
}
}
}
printf("%d\n", res);
}
int main()
{
while(scanf("%d%d", &N, &M) == 2 && N){
res = 0;
memset(dp, false, sizeof(dp));
for(int i = 0; i < N; i++){
scanf("%d", &a[i]);
}
for(int i = 0; i < N; i++){
scanf("%d", &c[i]);
}
solve();
}
return 0;
}
这个方法很好理解,1360MS,还凑和。
8.POJ 3046: Ant Counting
#include
#include
using namespace std;
int T, A, S, B;
const int M = 1e6;
int dp[2][100005];
int a[1005];
void solve()
{
dp[0][0] = dp[1][0] = 1;
for(int i = 0; i < T; i++){
for(int j = 1; j <= A; j++){
if(j - 1 >= a[i]){
dp[(i + 1) & 1][j] = (dp[(i + 1) & 1][j - 1] + dp[i & 1][j] - dp[i & 1][j - 1 - a[i]] + M) % M;
}
else{
dp[(i + 1) & 1][j] = (dp[(i + 1) & 1][j - 1] + dp[i & 1][j]) % M;
}
}
}
int res = 0;
for(int i = S; i <= B; i++){
res = (res + dp[T & 1][i]) % M;
}
printf("%d\n", res);
}
int main()
{
scanf("%d%d%d%d", &T, &A, &S, &B);
int p;
for(int i = 0; i < A; i++){
scanf("%d", &p);
a[p - 1]++;
}
solve();
return 0;
}
9.POJ 3181: Dollar Dayz
#include
int N, M;
long long dp1[105][1005], dp2[105][1005];
const long long Q = 1e18;
void solve()
{
dp2[0][0] = 1;
for(int i = 1; i <= M; i++){
for(int j = 0; j <= N; j++){
if(j - i < 0){
dp1[i][j] = dp1[i - 1][j];
dp2[i][j] = dp2[i - 1][j];
}
else{
dp1[i][j] = dp1[i - 1][j] + dp1[i][j - i] + (dp2[i - 1][j] + dp2[i][j - i]) / Q;
dp2[i][j] = (dp2[i - 1][j] + dp2[i][j - i]) % Q;
}
}
}
if(dp1[M][N]){
printf("%lld", dp1[M][N]);
}
printf("%lld\n", dp2[M][N]);
}
int main()
{
scanf("%d%d", &N, &M);
solve();
return 0;
}
(1)首先吧,这个题就很奇怪,他的答案和N的M划分数是一样的。
(2)这个结果会大的离谱,然后nb网友想了个法,分两个数组储存,一个存前18位,一个存后18位。16MS,1900K,还是蛮快的,内存占的也不多。
10.POJ 1065: Wooden Sticks
#include
#include
using namespace std;
int T, N;
const int INF = 1e8;
int dp[5005];
struct P
{
int l, w;
friend bool operator < (P a, P b)
{
if(a.l == b.l){
return a.w < b.w;
}
else{
return a.l < b.l;
}
}
};
P G[5005];
void solve()
{
for(int i = 0; i < N; i++){
dp[i] = INF;
}
sort(G, G + N);
for(int i = N - 1; i >= 0; i--){
*lower_bound(dp, dp + N, G[i].w) = G[i].w;
}
printf("%d\n", lower_bound(dp, dp + N, INF) - dp);
}
int main()
{
scanf("%d", &T);
while(T--){
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d%d", &G[i].l, &G[i].w);
}
solve();
}
return 0;
}
(1)链-反链-Dilworth定理(狄尔沃斯定理)
(2)Dilworth定理是个啥东东
(3)poj1065--Wooden Sticks--利用Dilworth定理
(4)就是说,寻找最大反链的元素个数。找最长下降子序列吗?倒着循环去找最长上升子序列就好。我感觉我写的的16MS已经很可以了,为什么有人0MS,你不是在开玩笑吗。
11.POJ 1631: Bridging signals
#include
#include
using namespace std;
int N, P, dp[40005], a[40005];
const int INF = 1e8;
void solve()
{
for(int i = 0; i < P; i++){
dp[i] = INF;
}
for(int i = 0; i < P; i++){
*lower_bound(dp, dp + P, a[i]) = a[i];
}
printf("%d\n", lower_bound(dp, dp + P, INF) - dp);
}
int main()
{
scanf("%d", &N);
while(N--){
scanf("%d", &P);
for(int i = 0; i < P; i++){
scanf("%d", &a[i]);
}
solve();
}
return 0;
}
(1)让人哭笑不得的题。读题20分钟,写题5分钟。
12.POJ 3666: Making the Grade
#include
#include
using namespace std;
int N, a[2005], b[2005];
long long dp[2005][2005];
long long abs0(int x)
{
if(x < 0){
return (long long)(-x);
}
else
return (long long)x;
}
void solve()
{
sort(b, b + N);
for(int i = 1; i < N; i++){
long long mn = dp[i - 1][0];
for(int j = 0; j < N; j++){
mn = min(mn, dp[i - 1][j]); //怎么说呢,当j是需要的那个j时,dp[i - 1][j]或之前已经到达最小,mn已经满足条件。
dp[i][j] = abs0(a[i] - b[j]) + mn;
}
}
long long ans = 1e18;
for(int i = 0; i < N; i++){
ans = min(ans, dp[N - 1][i]);
}
printf("%lld\n", ans);
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d", &a[i]);
b[i] = a[i];
}
solve();
return 0;
}
(1)dp[i][j]:前i个数最大值为j,数组的值为费用。那么,考虑一个问题,最优解的j一定是原来的高度中的一个值吗?因此只需让j表示数组下标,在后面运算的时候改为a[j]即可。
(2)dp的参数怎么设置?看看最优解只和哪些量有关。
(3)怎么讲,这道题挺难的,而且所给测试数据似乎只有nondecreasing的情况。
(4)ambiguating new declaration of... 指的是你想重载一个头文件里面已经有的一个函数,那么函数的返回类型必须和头文件里面的那个一样。要不就会报错,就是上面那行英文。
13.POJ 2392: Space Elevator
#include
#include
#include
using namespace std;
int K, dp[40005], maxa;
struct P
{
int h, a, c;
friend bool operator < (P x, P y)
{
return x.a < y.a;
}
};
P G[405];
void solve()
{
sort(G, G + K);
memset(dp, -1, sizeof(dp));
dp[0] = 0;
for(int i = 0; i < K; i++){
for(int j = 0; j <= G[i].a; j++){
if(dp[j] >= 0){
dp[j] = G[i].c;
}
else if(j < G[i].h || dp[j - G[i].h] <= 0){
dp[j] = -1;
//注:这里的判断条件是不可以加上j < G[i].a的,这会导致将其他不相关的j全变为dp[j] = -1,
//结果最后只能看到最后一组解。
}
else{
dp[j] = dp[j - G[i].h] - 1;
}
}
}
while(maxa >= 0){
if(dp[maxa] >= 0){
printf("%d\n", maxa);
break;
}
maxa--;
}
if(maxa < 0){
printf("%d\n", 0);
}
}
int main()
{
scanf("%d", &K);
for(int i = 0; i < K; i++){
scanf("%d%d%d", &G[i].h, &G[i].a, &G[i].c);
maxa = max(maxa, G[i].a);
}
solve();
return 0;
}
(1)这道题自己倒是尝试了一下,可惜有几个小地方写错了。还有,一定要注意最后输出的那一步。别再最后一步判断的时候忘记判断dp[0]和dp[maxa]。当然了,这道题数据很坑,可能会有无解的情况,dp全是-1,因此输出0。
(2)另外,一定要对数组排序的,不排序是过不去的。他和多重部分和问题不太一样。这个题数据的选择是有顺序的。
14.POJ 2184: Cow Exhibition
#include
#include
using namespace std;
const int INF = -1e6;
int N, s[105], f[105], dp[200000], maxs;
void solve()
{
for(int i = 0; i <= 200000; i++){
dp[i] = INF;
}
dp[100000] = 0;
//这一步很重要,设置了0点,左边智商和是负数,右边智商和是正数。
for(int i = 0; i < N; i++){
if(s[i] <= 0 && f[i] <= 0){
continue;
}
if(s[i] >= 0){
for(int j = 200000; j >= s[i]; j--){
if(dp[j - s[i]] > INF)
dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
}
}
//想想,完全背包和01背包区别在于第二重循环的方向。当s[i] < 0的时候,再倒着循环的话又成了完全背包。
else{
for(int j = 0; j <= 200000 + s[i]; j++){
if(dp[j - s[i]] > INF)
dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
}
}
}
int res = 0;
for(int j = 100000; j <= 200000; j++){
if(dp[j] >= 0){
res = max(res, dp[j] + j - 100000);
//j < 100000 的时候智商和是负数,因此要减掉10000;
}
}
printf("%d\n", res);
}
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%d%d", &s[i], &f[i]);
maxs += s[i];
}
solve();
return 0;
}
(1)你说这解法吧,也不是看不懂,网上啊,也都基本用的是这个法。最开始想到这个方法的人思路™真奇特。
1.如果向有map存键值对,值是字符串,怎么办?怎么查找,删除,遍历呢?
#include
#include
2.POJ 3614: Sunscreen
#include
#include
#include
using namespace std;
int C, L, res;
struct N
{
int s, t;
friend bool operator > (N a, N b)
{
return a.t > b.t;
}
};
struct P
{
int first, second;
friend bool operator < (P a, P b)
{
return a.first < b.first;
}
};
P G[2505];
priority_queue , greater > que;
void solve()
{
sort(G, G + L);
while(que.size()){
N cow = que.top();
que.pop();
for(int i = 0; i < L; i++){
if(G[i].first < cow.s || G[i].first > cow.t ||G[i].second == 0){
continue;
}
else{
G[i].second--;
res++;
break;
}
}
}
printf("%d\n", res);
}
int main()
{
scanf("%d%d", &C, &L);
N p;
for(int i = 0; i < C; i++){
scanf("%d%d", &p.s, &p.t);
que.push(p);
}
for(int i = 0; i < L; i++){
scanf("%d%d", &G[i].first, &G[i].second);
}
solve();
return 0;
}
(1)这道题的题意表述得很扯淡,其实意思很简单,就是让防晒霜的SPF值落在牛的SPF值里面。把牛按最大值排序,每次取最大值最小的牛,把防晒霜从小到大排序。很像区间的贪心问题,每次取出结束时间最早的。
3.POJ 2010: Moo University - Financial Aid
#include
#include
#include
using namespace std;
int N, C, F, cost;
struct P
{
int s, m;
};
bool com(P a, P b)
{
return a.s > b.s;
}
P G[100005];
int f1[100005], f2[100005];
void solve()
{
sort(G, G + C, com);
priority_queue que;
int cost1 = 0, cost2 = 0;
for(int i = 0; i < C; i++){
if(i < N / 2){
que.push(G[i].m);
cost1 += G[i].m;
continue;
}
f1[i] = cost1;
if(G[i].m < que.top()){
cost1 -= que.top();
cost1 += G[i].m;
que.pop();
que.push(G[i].m);
}
}
while(!que.empty()){
que.pop();
}
for(int i = C - 1; i >= 0; i--){
if(i >= C - N / 2){
que.push(G[i].m);
cost2 += G[i].m;
continue;
}
f2[i] = cost2;
if(G[i].m < que.top()){
cost2 -= que.top();
cost2 += G[i].m;
que.pop();
que.push(G[i].m);
}
}
int res = -1;
for(int i = N / 2; i < C - N / 2; i++){
if(f1[i] + f2[i] + G[i].m <= F){
res = G[i].s;
break;
}
}
printf("%d\n", res);
}
int main()
{
scanf("%d%d%d", &N, &C, &F);
for(int i = 0; i < C; i++){
scanf("%d%d", &G[i].s, &G[i].m);
}
solve();
return 0;
}
4.POJ 2236: Wireless Network
#include
#include
#include
#include
using namespace std;
int par[1006], rank[1006], N, d;
struct Point
{
int x, y;
};
vector repaired;
Point P[1006];
void init(int n)
{
for(int i = 0; i < n; i++){
par[i] = i;
rank[i] = 0;
}
}
int find(int x)
{
if(par[x] == x){
return x;
}
else{
return par[x] = find(par[x]);
}
}
void unite(int x, int y)
{
x = find(x);
y = find(y);
if(x != y){
if(rank[x] > rank[y]){
par[y] = x;
}
else{
par[x] = y;
if(rank[x] == rank[y]){
rank[x]++;
}
}
}
}
bool same(int x, int y)
{
return find(x) == find(y);
}
void repair(int co)
{
int sz = repaired.size();
for(int i = 0; i < sz; i++){
int idx = repaired[i];
if((int)pow((P[co].x - P[idx].x), 2) + (int)pow((P[co].y - P[idx].y), 2) <= d * d){
unite(co, idx);
}
}
}
int main()
{
scanf("%d%d", &N, &d);
init(N);
for(int i = 0; i < N; i++){
scanf("%d%d", &P[i].x, &P[i].y);
}
getchar();
char c;
while(scanf("%c", &c) == 1){
int a, b;
if(c == 'O'){
scanf("%d", &a);
repair(a - 1);
repaired.push_back(a - 1);
}
else if(c == 'S'){
scanf("%d%d", &a, &b);
if(same(a - 1, b - 1)){
printf("SUCCESS\n");
}
else{
printf("FAIL\n");
}
}
getchar();
}
}
(1)呵呵,自己独立写出来一道题真是少见。咋判断修过的点与其他点能不能联系呢?那就遍历修过的点呗。若有K条执行语句,复杂度应该不到K * N * logN。因为N的值不大,还是不会超时的。2954MS,不过题目限制是10000MS。
5.POJ 1703: Find them, Catch them
#include
#include
#include
using namespace std;
int par[200010], rank[200010], N, M, T;
void init(int n)
{
for(int i = 0; i < n; i++){
par[i] = i;
rank[i] = 0;
}
}
int find(int x)
{
if(par[x] == x){
return x;
}
else{
return par[x] = find(par[x]);
}
}
void unite(int x, int y)
{
x = find(x);
y = find(y);
if(x != y){
if(rank[x] > rank[y]){
par[y] = x;
}
else{
par[x] = y;
if(rank[x] == rank[y]){
rank[x]++;
}
}
}
}
bool same(int x, int y)
{
return find(x) == find(y);
}
int main()
{
scanf("%d", &T);
while(T--){
scanf("%d%d", &N, &M);
init(N * 2);
char c;
getchar();
int a, b;
for(int i = 0; i < M; i++){
scanf("%c", &c);
scanf("%d%d", &a, &b);
a--, b--;
getchar();
if(c == 'D'){
unite(a, b + N);
unite(a + N, b);
}
else if(c == 'A'){
if(same(a, b) || same(a + N, b + N)){
printf("In the same gang.\n");
}
else if(same(a, b + N) || same(a + N, b)){
printf("In different gangs.\n");
}
else{
printf("Not sure yet.\n");
}
}
}
}
return 0;
}
(1)自己写的,得提醒一下,几个帮派,init()函数就初始化几倍的N。但是,par与rank数组可别忘开大点啊,别只开到N + 5;而且我发现,两次runtime error都是由于数组越界造成的。
(2)和食物链那道题不同。食物链A,B,C之间的关系是单向的,不能A吃B,B还吃A。但是,这个题不同派别的关系是双向的。A和B不是一个派别,B和A也不是一个派别。因此会把unite(a, b + N)与unite(a + N, b)。
6.AOJ 2170: Marked Ancestor
#include
#include
#include
using namespace std;
int par[100005];
int N, Q;
typedef long long ll;
ll res;
int find(int x)
{
if(par[x] == x){
return x;
}
else{
return find(par[x]);
}
}
int main()
{
while(scanf("%d%d", &N, &Q) && N){
res = 0;
fill(par + 1, par + N + 1, 1);
for(int i = 2; i <= N; i++){
int p;
scanf("%d", &p);
par[i] = p;
}
for(int i = 0; i < Q; i++){
getchar();
char c = getchar();
int p;
scanf("%d", &p);
if(c == 'M'){
par[p] = p;
}
else if(c == 'Q'){
res += (ll)find(p);
}
}
printf("%lld\n", res);
}
return 0;
}
暴力搜索也能过,而且也简单,只用把并查集中的find函数稍微改一下就行。有人用线段树做,怎么讲。。。
1.单源最短路问题1(Bellman-Ford算法),书上是有向有环图算法,这里改为无向有环图算法,当然,有环都成立,无环也成立啊。
/*
input sample
7 10
1 2 2
1 3 5
3 2 4
2 4 6
3 4 2
2 5 10
4 6 1
5 6 3
5 7 5
6 7 9
1 7
output sample
16
*/
#include
#include
using namespace std;
int V, E;
const int maxe = 1000, maxv = 1000, INF = 1e8;
struct edge
{
int v1, v2, cost;
};
edge G[maxe];
int d[maxv], s, t;
void solve()
{
for(int i = 1; i <= V; i++){
d[i] = INF;
}
d[s] = 0;
while(1){
bool flag = false;
for(int i = 1; i <= E; i++){
edge e = G[i];
if(d[e.v1] != INF && d[e.v2] > d[e.v1] + e.cost){
d[e.v2] = d[e.v1] + e.cost;
flag = true;
}
if(d[e.v2] != INF && d[e.v1] > d[e.v2] + e.cost){
d[e.v1] = d[e.v2] + e.cost;
flag = true;
}
}
if(!flag) break;
}
printf("%d\n", d[t]);
}
int main()
{
scanf("%d%d", &V, &E);
for(int i = 1; i <= E; i++){
scanf("%d%d%d", &G[i].v1, &G[i].v2, &G[i].cost);
}
scanf("%d%d", &s, &t);
solve();
return 0;
}
2.POJ 3723 Conscription
我真的不想吐槽,细节是多么的重要。
#include
#include
#include
using namespace std;
int N, M, R, V, T, par[20005], rank[20005], x[50005], y[50005], co[50005];
//我不想吐槽,细节是多么的重要,par放的是男的和女的,最大可达20000。par,rank数组开小了就会RE。
void init(int n)
{
for(int i = 0; i < n; i++){
par[i] = i;
rank[i] = 0;
}
}
int find(int x)
{
if(par[x] == x){
return x;
}
else{
return par[x] = find(par[x]);
}
}
void unite(int x, int y)
{
x = find(x);
y = find(y);
if(x == y){
return;
}
if(rank[x] < rank[y]){
par[x] = y;
}
else{
par[y] = x;
if(rank[x] == rank[y]){
rank[x]++;
}
}
}
bool same(int x, int y)
{
return find(x) == find(y);
}
struct edge
{
int u, v, cost;
friend bool operator < (edge a, edge b)
{
return a.cost < b.cost;
}
};
edge G[50010];
//我不想吐槽,细节是多么的重要,这个题R和V是真的容易搞混。G数组开小了就会RE。
int tree()
{
init(V);
int res = 0;
sort(G, G + R);
//我真的真的真的不想吐槽,细节是多么多么多么的重要,R和V是真的容易搞混。这个排序写成G + V他就会WA半天。
for(int i = 0; i < R; i++){
edge e = G[i];
if(!same(e.u, e.v)){
res += e.cost;
unite(e.u, e.v);
}
}
return res;
}
void solve()
{
V = M + N;
for(int i = 0; i < R; i++){
G[i] = (edge){x[i], y[i] + N, -co[i]};
}
printf("%d\n", 10000 * V + tree());
}
int main()
{
scanf("%d", &T);
while(T--){
scanf("%d%d%d", &N, &M, &R);
for(int i = 0; i < R; i++){
scanf("%d%d%d", &x[i], &y[i], &co[i]);
//我不想吐槽,细节是多么的重要,男女编号是从0开始的。
}
solve();
}
return 0;
}
3.AOJ 0189: Convenient Location
#include
#include
#include
using namespace std;
int cost[15][15];
int N, V;
void solve()
{
for(int k = 0; k <= V ; k++){
for(int i = 0; i <= V; i++){
for(int j = 0; j <= V; j++){
cost[i][j] = min(cost[i][j], cost[i][k] + cost[k][j]);
}
}
}
int city = 0, res = 1e7;
for(int i = 0; i <= V; i++){
int dis = 0;
for(int j = 0; j <= V; j++){
dis += cost[i][j];
}
if(res > dis){
city = i;
res = dis;
}
}
printf("%d %d\n", city, res);
}
int main()
{
int s, t, co;
while(scanf("%d", &N) && N){
V = 0;
//多组数据输入,一定一定要考虑每个全局变量是否要初始化,切记!!!
for(int i = 0; i < 10; i++){
for(int j = 0; j < 10; j++){
cost[i][j] = 1e7;
}
cost[i][i] = 0;
}
for(int i = 0; i < N; i++){
scanf("%d%d%d", &s, &t, &co);
cost[s][t] = cost[t][s] = co;
V = max(V, s);
V = max(V, t);
}
solve();
}
return 0;
}
(1)这个题可以转化为一点到任意一点最短距离问题,若用优先队列可以降到n * n * logn,此题数据较弱,没用那个方法。
(2)//多组数据输入,一定一定要考虑每个全局变量是否要初始化,切记!!!
4.POJ 3169 Layout
#include
#include
#include
using namespace std;
int N, ML, MD;
const int maxn = 1005, maxl = 10005, INF = 1e8;
int AL[maxl], BL[maxl], DL[maxl], AD[maxl], BD[maxl], DD[maxl], d[maxn];
void solve()
{
fill(d, d + N, INF);
d[0] = 0;
for(int k = 0; k < N; k++){
//有负圈,不可以用while循环,而不管有无负圈,最多更新N次。
for(int i = 0; i < N - 1; i++){
if(d[i + 1] < INF){
d[i] = min(d[i], d[i + 1]);
}
}
for(int i = 0; i < ML; i++){
if(d[AL[i] - 1] < INF){
d[BL[i] - 1] = min(d[BL[i] - 1], d[AL[i] - 1] + DL[i]);
}
}
for(int i = 0; i < MD; i++){
if(d[BD[i] - 1] < INF){
d[AD[i] - 1] = min(d[AD[i] - 1], d[BD[i] - 1] - DD[i]);
}
}
}
int res = d[N - 1];
if(d[0] < 0){
printf("%d", -1);
return;
}
if(res == INF){
printf("%d", -2);
return;
}
printf("%d\n", res);
}
int main()
{
scanf("%d%d%d", &N, &ML, &MD);
for(int i = 0; i < ML; i++){
scanf("%d%d%d", &AL[i], &BL[i], &DL[i]);
}
for(int i = 0; i < MD; i++){
scanf("%d%d%d", &AD[i], &BD[i], &DD[i]);
}
solve();
return 0;
}
(1)这道题真的看不懂,按照书上的答案AC了,差分约束是什么鬼,以后再看看。
5.POJ 2139: Six Degrees of Cowvin Bacon
#include
#include
#include
using namespace std;
const int INF = 1e8;
int N, M, n, cost[305][305], appear[305];
void solve()
{
for(int k = 1; k <= N; k++){
for(int i = 1; i <= N; i++){
for(int j = 1; j <= N; j++){
cost[i][j] = min(cost[i][j], cost[i][k] + cost[k][j]);
}
}
}
int res = INF;
for(int i = 1; i <= N; i++){
int tmp = 0;
for(int j = 1; j <= N; j++){
tmp += cost[i][j];
}
res = min(res, tmp);
}
res = res * 100 / (N - 1);
printf("%d\n", res);
}
int main()
{
scanf("%d%d", &N, &M);
for(int i = 1; i <= N; i++){
for(int j = 1; j <= N; j++){
cost[i][j] = INF;
}
}
while(M--){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &appear[i]);
}
for(int i = 1; i <= n; i++){
for(int j = i + 1; j <= n; j++){
cost[appear[i]][appear[j]] = 1;
cost[appear[j]][appear[i]] = 1;
}
}
}
for(int i = 1; i <= N; i++){
cost[i][i] = 0;
}
solve();
return 0;
}
水题一道,可喜可贺,0MS。
6.POJ 3259: Wormholes
#include
using namespace std;
int F, N, M, W;
const int INF = 1e8;
int cost[505][505];
void solve()
{
for(int k = 1; k <= N; k++){
for(int i = 1; i <= N; i++){
for(int j = 1; j <= N; j++){
if(cost[i][j] > cost[i][k] + cost[k][j]){
cost[i][j] = cost[i][k] + cost[k][j];
}
}
if(cost[i][i] < 0){
printf("YES\n");
return;
}
}
}
printf("NO\n");
}
int main()
{
scanf("%d", &F);
int s, e, t;
while(F--){
scanf("%d%d%d", &N, &M, &W);
for(int i = 1; i <= N; i++){
for(int j = 1; j <= N; j++){
cost[i][j] = INF;
}
cost[i][i] = 0;
}
//把这个cost[i][i] = 0放前面,万一只有一个负环怎么办?
for(int i = 1; i <= M; i++){
scanf("%d%d%d", &s, &e, &t);
if(cost[s][e] > t){
cost[s][e] = cost[e][s] = t;
}
}
for(int i = 1; i <= W; i++){
scanf("%d%d%d", &s, &e, &t);
cost[s][e] = -t;
}
solve();
}
return 0;
}
(1)这个题,首先要提醒,可能出现重边,要选择最小的那条边。
(2)其次,我发现把min换成if判断语句,时间至少快了400MS。
(3)欸,输出是"YES\n"还是"Yes\n"啊,看清楚行不!
7.POJ 3268: Silver Cow Party
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1005, INF = 1e8;
struct edge
{
int to, cost;
};
vector G[maxn];
vector GR[maxn];
//将所有道路反转。
typedef pair P;
int N, M, X, d[maxn], dr[maxn];
void solve()
{
fill(d + 1, d + N + 1, INF);
fill(dr + 1, dr + N + 1, INF);
d[X] = dr[X] = 0;
priority_queue, greater
> que;
priority_queue
, greater
> quer;
que.push(P(0, X));
quer.push(P(0, X));
while(!que.empty()){
P p = que.top();
que.pop();
int v = p.second;
if(d[v] < p.first){
continue;
}
int sz = G[v].size();
for(int i = 0; i < sz; i++){
edge e = G[v][i];
if(d[e.to] > d[v] + e.cost){
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to], e.to));
}
}
}
while(!quer.empty()){
P p = quer.top();
quer.pop();
int v = p.second;
if(dr[v] < p.first){
continue;
}
int sz = GR[v].size();
for(int i = 0; i < sz; i++){
edge e = GR[v][i];
if(dr[e.to] > dr[v] + e.cost){
dr[e.to] = dr[v] + e.cost;
quer.push(P(dr[e.to], e.to));
}
}
}
int res = 0;
for(int i = 1; i <= N; i++){
res = max(res, d[i] + dr[i]);
}
printf("%d\n", res);
}
int main()
{
scanf("%d%d%d", &N, &M, &X);
int s, t, co;
for(int i = 1; i <= M; i++){
scanf("%d%d%d", &s, &t, &co);
G[s].push_back({t, co});
GR[t].push_back({s, co});
}
solve();
return 0;
}
水题水题,将单行道方向反过来就是任意一点到指定一点的最小距离。
8.POJ 1258: Agri-Net
#include
#include
#include
using namespace std;
const int maxn = 105;
int N, par[105], rank[105];
struct edge
{
int u, v, cost;
friend bool operator < (edge e1, edge e2)
{
return e1.cost < e2.cost;
}
};
edge G[maxn * maxn];
void init(int n)
{
for(int i = 0; i < n; i++){
par[i] = i;
rank[i] = 0;
}
}
int find(int x)
{
if(par[x] == x){
return x;
}
else{
return par[x] = find(par[x]);
}
}
void unite(int x, int y)
{
x = find(x);
y = find(y);
if(x == y){
return;
}
if(rank[x] < rank[y]){
par[x] = y;
}
else{
par[y] = x;
if(rank[x] == rank[y]){
rank[x]++;
}
}
}
bool same(int x, int y)
{
return find(x) == find(y);
}
void solve()
{
int sz = N * N;
init(N);
sort(G, G + sz);
int res = 0;
for(int i = 0; i < sz; i++){
edge e = G[i];
if(!same(e.u, e.v)){
res += e.cost;
unite(e.u, e.v);
}
}
printf("%d\n", res);
}
int main()
{
int co;
while(scanf("%d", &N) == 1){
for(int i = 0; i < N; i++){
for(int j = 0; j < N; j++){
scanf("%d", &co);
G[i * N + j] = {i, j, co};
}
}
solve();
}
return 0;
}
最小生成树经典问题。水题。但是,有需要注意的地方。
(1) 这道题是不可以用vector的,因为貌似sort不支持vector;
(2)vector中的clear()可以改变size,但不可以改变capacity,但是,这不影响。我验证过了,不论是int还是struct,clear这个函数都可以把vector中的东西清空,直接用就可以了。
9.POJ 2377: Bad Cowtractors
#include
#include
using namespace std;
const int maxn = 1005, maxm = 20005, INF = 1e8;
int N, M, par[maxn], rank[maxn];
struct edge
{
int u, v, cost;
friend bool operator < (edge e1, edge e2)
{
return e1.cost > e2.cost;
}
};
edge G[maxm];
void init(int n)
{
for(int i = 1; i <= n; i++){
par[i] = i;
rank[i] = 0;
}
}
int find(int x)
{
if(par[x] == x){
return x;
}
return par[x] = find(par[x]);
}
void unite(int x, int y)
{
x = par[x];
y = par[y];
if(x == y){
return;
}
if(rank[x] < rank[y]){
par[x] = y;
}
else{
par[y] = x;
if(rank[x] == rank[y]){
rank[x]++;
}
}
}
bool same(int x, int y)
{
return find(x) == find(y);
}
void solve()
{
init(N);
sort(G, G + M);
int res = 0;
for(int i = 0; i < M; i++){
edge e = G[i];
if(!same(e.u, e.v)){
unite(e.u, e.v);
res += e.cost;
}
}
for(int i = 1; i <= N; i++){
for(int j = i + 1; j <= N; j++){
if(!same(i, j)){
printf("%d\n", -1);
return;
}
}
}
printf("%d\n", res);
}
int main()
{
scanf("%d%d", &N, &M);
int s, t, co;
for(int i = 0; i < M; i++){
scanf("%d%d%d", &s, &t, &co);
G[i] = {s, t, co};
}
solve();
return 0;
}
(1)这道题那个sort那个地方一定要记住,sort(G, G + M),M是路径数,可不是结点数。
(2)这道题复杂度是n^2,本以为会很慢,结果63MS
(3)最大生成树,做法和最小生成树差不多。
10.POJ 2395: Out of Hay
#include
#include
using namespace std;
const int maxn = 2005, maxm = 10005;
int N, M, par[maxn], rank[maxn];
struct edge
{
int u, v, cost;
bool friend operator < (edge e1, edge e2)
{
return e1.cost < e2.cost;
}
};
edge G[maxm];
void init(int n)
{
for(int i = 1; i <= n; i++){
par[i] = i;
rank[i] = 0;
}
}
int find(int x)
{
if(par[x] == x){
return x;
}
return par[x] = find(par[x]);
}
void unite(int x, int y)
{
x = par[x];
y = par[y];
if(x == y){
return;
}
else{
if(rank[x] < rank[y]){
par[x] = y;
}
else{
par[y] = x;
if(rank[x] == rank[y]){
rank[x]++;
}
}
}
}
bool same(int x, int y)
{
return find(x) == find(y);
}
void solve()
{
init(N);
sort(G, G + M);
int res = 0;
for(int i = 0; i < M; i++){
edge e = G[i];
if(!same(e.u, e.v)){
unite(e.u, e.v);
res = max(res, e.cost);
}
}
printf("%d\n", res);
}
int main()
{
scanf("%d%d", &N, &M);
int s, t, co;
for(int i = 0; i < M; i++){
scanf("%d%d%d", &s, &t, &co);
G[i] = {s, t, co};
}
solve();
return 0;
}
水题。
11.
AOJ 2249: Road Construction
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 10005, maxm = 20005, INF = 1e8;
int d[maxn], value[maxn];
int N, M;
struct edge
{
int to, cost, val;
};
vector G[maxm];
typedef pair P;
void solve()
{
d[1] = value[1] = 0;
priority_queue, greater
> que;
que.push(P(0, 1));
while(!que.empty()){
P p = que.top();
que.pop();
int v = p.second;
if(d[v] < p.first){
continue;
}
int sz = G[v].size();
for(int i = 0; i < sz; i++){
edge e = G[v][i];
if(d[e.to] > e.cost + d[v]){
d[e.to] = e.cost + d[v];
value[e.to] = e.val;
que.push(P(d[e.to], e.to));
}
else if(d[e.to] == e.cost + d[v]){
value[e.to] = min(value[e.to], e.val);
}
}
}
int res = 0;
for(int i = 1; i <= N; i++){
res += value[i];
}
printf("%d\n", res);
}
int main()
{
while(scanf("%d%d", &N, &M) == 2 && N){
fill(d + 1, d + N + 1, INF);
fill(value + 1, value + N + 1, INF);
int s, t, c, v;
for(int i = 1; i <= M; i++){
scanf("%d%d%d%d", &s, &t, &c, &v);
G[s].push_back({t, c, v});
G[t].push_back({s, c, v});
}
solve();
//清空G;
for(int i = 1; i <= N; i++){
G[i].clear();
}
}
return 0;
}
这道题略微有点绕,不过也没啥,只需先保证最短路,然后在此前提下将对应的花费保存一下就行。
12.
AOJ 2200: Mr. Rito Post Office
#include
#include
#include
using namespace std;
const int maxn = 205, maxm = 10005, maxr = 1005, INF = 2e8;
int cost1[maxn][maxn], cost2[maxn][maxn], pass[maxr], dp[maxr][maxn];
int N, M, R;
void solve()
{
for(int k = 1; k <= N; k++){
for(int i = 1; i <= N; i++){
for(int j = 1; j <= N; j++){
cost1[i][j] = min(cost1[i][j], cost1[i][k] + cost1[k][j]);
cost2[i][j] = min(cost2[i][j], cost2[i][k] + cost2[k][j]);
}
}
}
for(int i = 1; i <= R; i++){
fill(dp[i] + 1, dp[i] + N + 1, INF);
}
for(int i = 1; i <= N; i++){
dp[1][i] = cost2[pass[1]][i] + cost1[i][pass[1]];
}
for(int i = 1; i < R; i++){
for(int j = 1; j <= N; j++){
for(int k = 1; k <= N; k++){
if(j != k){
dp[i + 1][k] = min(dp[i + 1][k], dp[i][j] + cost1[pass[i]][j] + cost2[j][k] + cost1[k][pass[i + 1]]);
}
else{
dp[i + 1][k] = min(dp[i + 1][k], dp[i][k] + cost1[pass[i]][pass[i + 1]]);
}
}
}
}
printf("%d\n", *min_element(dp[R] + 1, dp[R] + N + 1));
}
int main()
{
while(scanf("%d%d", &N, &M) && N){
for(int i = 1; i <= N; i++){
fill(cost1[i] + 1, cost1[i] + N + 1, INF);
fill(cost2[i] + 1, cost2[i] + N + 1, INF);
cost1[i][i] = cost2[i][i] = 0;
}
int s, t, co, kind;
for(int i = 0; i < M; i++){
scanf("%d%d%d", &s, &t, &co);
getchar();
char c = getchar();
if(c == 'L'){
cost1[s][t] = cost1[t][s] = min(cost1[s][t], co);
}
else{
cost2[s][t] = cost2[t][s] = min(cost2[s][t], co);
}
}
scanf("%d", &R);
for(int i = 1; i <= R; i++){
scanf("%d", &pass[i]);
}
solve();
}
return 0;
}
挺难的,这有题解:AOJ 2200 Mr. Rito Post Office 题解 《挑战程序设计竞赛》
注意,在初期状态下,利藤和船都存在于港口城市z1,而不是城市1。
13.AOJ 2224: Save your cat
#include
#include
#include
using namespace std;
const int maxn = 10005, maxm = 50005;
int x[maxn], y[maxn], par[maxn], ranking[maxn];
int N, M;
double res;
struct edge
{
int u, v;
double cost;
};
edge G[maxm];
bool comp(edge e1, edge e2)
{
return e1.cost > e2.cost;
}
void init(int n)
{
for(int i = 1; i <= n; i++){
par[i] = i;
ranking[i] = 0;
}
}
int find(int x)
{
if(par[x] == x){
return x;
}
else{
return par[x] = find(par[x]);
}
}
void unite(int x, int y)
{
x = find(x);
y = find(y);
if(x == y){
return;
}
if(ranking[x] < ranking[y]){
par[x] = y;
}
else{
par[y] = x;
if(ranking[x] == ranking[y]){
ranking[x]++;
}
}
}
bool same(int x, int y)
{
return find(x) == find(y);
}
void solve()
{
init(N);
sort(G, G + M, comp);
for(int i = 0; i < M; i++){
edge e = G[i];
if(!same(e.u, e.v)){
unite(e.u, e.v);
res -= e.cost;
}
}
printf("%.3f", res);
}
int main()
{
scanf("%d%d", &N, &M);
for(int i = 1; i <= N; i++){
scanf("%d%d", &x[i], &y[i]);
}
int s, t;
for(int i = 0; i < M; i++){
scanf("%d%d", &s, &t);
double d = sqrt((double)((x[s] - x[t]) * (x[s] - x[t]) + (y[s] - y[t]) * (y[s] - y[t])));
G[i] = {s, t, d};
res += d;
}
solve();
return 0;
}
(1)从C++11开始,不能用rank作为数组名了,因为多了一个std::rank函数。因此以后并查集都改用ranking吧。
(2)最大生成树,还得自定义com函数,用friend bool operator不行,因为sort不认识大于号。
1.AOJ 0005: GCD and LCM
#include
int gcd(int a, int b)
{
if(b == 0){
return a;
}
return gcd(b, a % b);
}
int main()
{
int a, b;
while(scanf("%d%d", &a, &b) == 2){
int res1 = gcd(a, b);
int res2 = a * (b / res1);
printf("%d %d\n", res1, res2);
}
return 0;
}
(1)基础题型,但是要说两点,第一是虽然题目说LCM(a, b) <= 2,000,000,000,但是算LCM是a * b / gcd(a, b),a * b还是可能把 int 撑爆。第二是即使这样,我们仍然可以用int,只需a * (b / gcd(a, b))即可。
2.POJ 2429: GCD & LCM Inverse
POJ 2429 GCD & LCM Inverse 题解 《挑战程序设计竞赛》
int64内Miller-Rabin素数测试和Pollard_Rho_因数分解算法实现
(1)(a*b)%m 是等价于 a%m * b%m的
(2)此题包含了大数a*b%m,a^b%m,十分重要的两个算法。
(3)这道题太超了,真的不会。
放几个模板吧
(1)大数(a * b) % m
ll multi_mod(ll a, ll b, ll n)
{
a %= n;
b %= n;
ll res = 0;
while(b)
{
if(b & 1)
{
res += a;
if(res >= n) res -= n;
}
a <<= 1;
if(a >= n) a -= n;
b >>= 1;
}
return res;
}
(2)大数(a ^ b) % m
ll pow_mod(ll x, ll n, ll mod)
{
if(n == 1) return x % mod;
x %= mod;
ll tmp = x;
ll res = 1;
while(n > 0)
{
if(n & 1) res = multi_mod(res, tmp, mod);
tmp = multi_mod(tmp, tmp, mod);
n >>= 1;
}
return res;
}
(3)
bool robin_miller(ll n) //判断是否素数
{
if(n < 2) return false;
if(n == 2) return true;
if(!(n & 1)) return false;
ll u = n - 1, t = 0;
while(!(u & 1)) u >>= 1, t++;
if(t >= 1 && (u & 1) == 1)
{
for(int i = 0; i < s; i++)
{
ll a = rand() % (n-1) + 1;
if(witness(a, n, u, t)) return false; //不是素数
}
}
return true; //是素数
}
(4)完整代码
/*
translation:
给出两个数的gcd,lcm的值,求两个数分别是多少?有多解时输出两数和最小的一组。
solution:
miller-robin和pollard素因子分解算法
这道题可以参考poj1811(模板),根据规律a*b == gcd(a,b)*lcm(a,b)很容易想出分解质因数
然后凑出a,b来暴力比较求出答案。但是由于数据范围太大。所以常规的素数分解算法不行。套上pollard
算法的模板即可。然后dfs求出最靠近sqrt(y)但是不超过y的数字,这个数字就是所求的第一个数。
注意一开始x,y相乘可能溢出,根据x,y的特性,可以用分解y/x的质因数来代替。因为y*x和y/x只有次数不同。
note:
# 注意原来pollard模板不能对1分解质因数,会RE。在原来的基础上在一开始加上了对n==1的情况的处理即可
date:
2016.10.26
*/
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int max_prime = 1e6;
const int s = 20;
typedef long long ll;
vector factor;
vector prime_factor;
ll x, y; //x:gcd, y:lcm
ll multi_mod(ll a, ll b, ll n)
{
a %= n;
b %= n;
ll res = 0;
while(b)
{
if(b & 1)
{
res += a;
if(res >= n) res -= n;
}
a <<= 1;
if(a >= n) a -= n;
b >>= 1;
}
return res;
}
ll pow_mod(ll x, ll n, ll mod)
{
if(n == 1) return x % mod;
x %= mod;
ll tmp = x;
ll res = 1;
while(n > 0)
{
if(n & 1) res = multi_mod(res, tmp, mod);
tmp = multi_mod(tmp, tmp, mod);
n >>= 1;
}
return res;
}
bool witness(ll a, ll n, ll u, ll t)
{
ll res = pow_mod(a, u, n);
ll last = res;
for(int i = 0; i < t; i++)
{
res = multi_mod(res, res, n);
if(res == 1 && last != 1 && last != n-1) return true;
last = res;
}
if(res != 1) return true;
else return false;
}
bool robin_miller(ll n) //判断是否素数
{
if(n < 2) return false;
if(n == 2) return true;
if(!(n & 1)) return false;
ll u = n - 1, t = 0;
while(!(u & 1)) u >>= 1, t++;
if(t >= 1 && (u & 1) == 1)
{
for(int i = 0; i < s; i++)
{
ll a = rand() % (n-1) + 1;
if(witness(a, n, u, t)) return false; //不是素数
}
}
return true; //是素数
}
ll gcd(ll a, ll b)
{
if(a == 0) return 1;
if(a < 0) return gcd(-a, b);
while(b)
{
ll t = a % b;
a = b;
b = t;
}
return a;
}
ll pollard_rho(ll x, ll c)
{
ll i = 1, x0 = rand() % x;
ll y = x0;
ll k = 2;
for(;;)
{
i++;
x0 = (multi_mod(x0, x0, x) + c) % x;
ll d = gcd(y - x0, x); //这里传给gcd的参数可能负数,注意分别讨论
if(d != 1 && d != x) return d; //这里表明找到了一个因子
if(y == x0) return x;
if(i == k)
{
y = x0;
k += k;
}
}
}
void find_fac(ll n)
{
if(n == 1) return;
if(robin_miller(n))
{
prime_factor.push_back(n);
return;
}
ll p = n;
while(p >= n) p = pollard_rho(p, rand() % (n-1) + 1);
find_fac(p);
find_fac(n / p);
}
ll dfs(int p, int len, ll res, ll m)
{
if(res > m) return 0;
if(p == len || res == m) return res;
ll tmp1 = dfs(p+1, len, res, m), tmp2;
if(res * factor[p] + 0.0 > m) return tmp1;
else
{
tmp2 = dfs(p+1, len, res*factor[p], m);
return tmp1 > tmp2 ? tmp1 : tmp2;
}
}
int main()
{
//freopen("in.txt", "r", stdin);
while(cin >> x >> y)
{
srand(time(NULL));
prime_factor.clear();
factor.clear();
y /= x;
find_fac(y);
ll tmp = y;
for(int i = 0; i < prime_factor.size(); i++)
{
ll res = 1;
while(tmp % prime_factor[i] == 0)
res *= prime_factor[i], tmp /= prime_factor[i];
if(res != 1) factor.push_back(res);
}
int len = factor.size();
ll m = (ll)sqrt(y + 0.0);
ll a = dfs(0, len, 1, m);
ll b = y / a;
printf("%lld %lld\n", a * x, b * x);
}
return 0;
}
3.POJ 1930: Dead Fraction