课后习题(一)——第1~2章

我说啊,你这个long long 写成 int 导致 WA,== 写成 = WA,多少次啦!为啥不长点记性?!

蒟蒻对于这本书的会写的课后习题编了菜菜的代码,不会写的请教了大佬然后整理了代码,并胆大包天居然敢把部分例题改成了菜菜的代码,不过这些代码都是可以在相应OJ上提交并通过的(注明WA的除外)。

目录

第二章

2.1

dfs

bfs

穷竭搜索

2.2

2.3

2.4

2.5

2.6


第二章

2.1

dfs

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;
}

bfs

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
#include
using namespace std;
map dp;
int d[] = {1, -1, 4, -4};
void bfs()
{
    dp["01234567"] = 0;
    queue que;
    que.push("01234567");
    while(que.size()){
        string now = que.front();  //这里的string用法和python太像了~~~
        que.pop();
        int p;
        for(int i = 0; i < 8; i++){
            if(now[i] == '0'){
                p = i;
                break;
            }
        }
        for(int i = 0; i < 4; i++){
            int np = p + d[i];
            if(np >= 0 && np < 8 && !(p == 3 && i == 0) && !(p == 4 && i == 1)){
                string next = now;
                swap(next[np], next[p]);
                if(dp.find(next) == dp.end()){
                    dp[next] = dp[now] + 1;
                    que.push(next);
                }
            }
        }
    }
}
int main()
{
    bfs();
    int _;
    while(scanf("%d", &_) == 1){
        string s;
        s = '0' + _;
        for(int i = 0; i < 7; i++){
            scanf("%d", &_);
            s += '0' + _;
        }
        printf("%d\n", dp[s]);
    }
    return 0;
}

(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用行数。至于每一列怎么反转,代码中有描述。而且,每一行与每一列的翻面是相对独立的,因为某一列与某一行翻转顺序不影响最终翻转结果。

2.2

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;
}

2.3

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)你说这解法吧,也不是看不懂,网上啊,也都基本用的是这个法。最开始想到这个方法的人思路™真奇特。 

2.4

1.如果向有map存键值对,值是字符串,怎么办?怎么查找,删除,遍历呢?

#include
#include
using namespace std;
int main()
{
    map m;
    m.insert(make_pair(1, "ONE"));
    m[100] = "HUNDRED";

    map::iterator ite;
    ite = m.find(1);
    if(ite == m.end())
        puts("Not found");
    m.erase(1);
    for(ite = m.begin(); ite != m.end(); ++ite){
        printf("%d: %s\n", ite->first, ite->second);
    }
    return 0;
}

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函数稍微改一下就行。有人用线段树做,怎么讲。。。

2.5

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不认识大于号。 

2.6

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

 

你可能感兴趣的:(挑战程序设计竞赛)