NOIP 2014 Senior 3

来源:互联网 发布:阿里云cdn价格下调25 编辑:程序博客网 时间:2024/06/15 14:52

题目1
题目2
思路1
看到50分都是搜索的范围,于是我就爆搜了。恭喜,我又写对了(不容易啊,所以要多检查,增加静态查错能力),所以还是贴一个代码吧。

const int maxn = 10005;int n, m, k;INT X[maxn];INT Y[maxn];struct pipe{    INT P;    INT L;    INT H;    bool operator< (const pipe& b) const    {        return P < b.P;    }} pipes[maxn];INT minClick = 0x7fffffff;INT maxThrough;bool bWin;INT pipeIndex;INT click;void dfs(int h, int x = 1){    if (click > minClick) return;    if (x == n + 1)    {        minClick = std::min(minClick, click);        bWin = true;        return;    }    bool onPipe = false;    INT maxH = m;    INT minH = 0;    if (pipeIndex < k && pipes[pipeIndex].P == x)    {        onPipe = true;        maxH = pipes[pipeIndex].H;        minH = pipes[pipeIndex].L;        pipeIndex++;    }    if (h - Y[x] > minH && h - Y[x] < maxH)    {        if (onPipe) maxThrough = std::max(pipeIndex, maxThrough);        dfs(h - Y[x], x + 1);    }    for (int i = X[x], j = 1; h + i - X[x] < m; i += X[x], j++)    {        int next = std::min(m, h + i);        if (next > minH && (!onPipe || next < maxH))        {            click += j;            if (onPipe) maxThrough = std::max(pipeIndex, maxThrough);            dfs(next, x + 1);            click -= j;        }    }    if (onPipe) pipeIndex--;}void run(){    n = readIn();    m = readIn();    k = readIn();    for (int i = 1; i <= n; i++)    {        X[i] = readIn();        Y[i] = readIn();    }    for (int i = 0; i < k; i++)    {        pipes[i].P = readIn();        pipes[i].L = readIn();        pipes[i].H = readIn();    }    std::sort(pipes, pipes + k);    for (int i = m; i >= 1; i--)        dfs(i);    if (bWin)        cout << 1 << endl << minClick << endl;    else        cout << 0 << endl << maxThrough << endl;}

我本来还以为这道题是通过神级剪枝过的,但是我就想到一个最优化剪枝,其它的。。。

思路2 DP
说好的第一天第三题考考搜索呢(其实没有人说)!谈到搜索,我们自然会想到DP ——DP的问题引入不都是从搜索会超时开始的吗。这道题也可以DP,状态也很好描述,就是一个点的坐标。所以我们设f[x][y]代表到坐标(x, y)时最小的点击次数。应该还是很好想到一个状态转移方程,就是根据x - 1时的坐标和点击的次数k来计算横坐标为x时对应的位置。但很明显,由于引入了k,时间复杂度将变为O(nm2),因此不可取。
其实,这道题有点像背包dp:向下是01背包,向上是完全背包,只不过有一些特别的限制。为了解决刚刚k那个问题,我们可以参考(没用滚动数组的)完全背包的做法:既可以在未选择物品i的状态中选,也可以在选择了物品i的状态中选。换在这道题上,就是既可以通过在横坐标为x - 1时点击一次到横坐标为x的对应位置,也可以在横坐标为x的地方继续点一次。
所以怎么处理管子呢?我们不如先转移状态,然后再将限制加上。最后,一定要记住先更新向上的,再更新向下的,因为根据刚刚的思路会向下向上同时进行,不符合题意。
当然,再来个滚动数组就好了。

参考代码(渣)

#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <string>#include <stack>#include <queue>#include <deque>#include <map>#include <set>using std::cin;using std::cout;using std::endl;typedef int INT;inline INT readIn(){    bool minus = false;    INT a = 0;    char ch = getchar();    while (!(ch == '-' || ch >= '0' && ch <= '9')) ch = getchar();    if (ch == '-')    {        minus = true;        ch = getchar();    }    while (ch >= '0' && ch <= '9')    {        a *= 10;        a += ch;        a -= '0';        ch = getchar();    }    if (minus) a = -a;    return a;}const int maxn = 10005;const int maxm = 1005;int n, m, k;INT X[maxn];INT Y[maxn];INT L[maxn];INT H[maxn];int nPipe[maxn];INT f[2][maxm];void run(){    n = readIn();    m = readIn();    k = readIn();    H[0] = m + 1;    for (int i = 1; i <= n; i++)    {        X[i] = readIn();        Y[i] = readIn();        L[i] = 0;        H[i] = m + 1;    }    for (int i = 0; i < k; i++)    {        INT P = readIn();        nPipe[P] = true;        L[P] = readIn();        H[P] = readIn();    }    for (int i = 1; i <= n; i++) //走到nPipe[i]时过的管子数    {        if (nPipe[i]) nPipe[i] = nPipe[i - 1] + 1;        else nPipe[i] = nPipe[i - 1];    }    const INT INF = 0x3f3f3f3f; //往往极大值策略比-1策略还要有效    INT farthest = 0;    for (int i = 1; i <= n; i++)    {        int cnt = i & 1;        int last = (i + 1) & 1;        memset(f[cnt], 0x3f, sizeof(f[cnt]));        for (int j = X[i]; j <= m; j++) //不碰顶的        {            f[cnt][j] = std::min(f[cnt][j], f[last][j - X[i]] + 1);            f[cnt][j] = std::min(f[cnt][j], f[cnt][j - X[i]] + 1);            if (j > L[i] && j < H[i] && f[cnt][j] != INF) farthest = i; //若这个地方可以到,就更新走到的最远的地方        }        for (int j = m - X[i]; j <= m; j++) //碰顶的        {            f[cnt][m] = std::min(f[cnt][m], f[last][j] + 1);            f[cnt][m] = std::min(f[cnt][m], f[cnt][j] + 1);            if (j > L[i] && j < H[i] && f[cnt][j] != INF) farthest = i;        }        for (int j = L[i] + 1; j < H[i]; j++) //下落        {            if (j + Y[i] <= m)            {                f[cnt][j] = std::min(f[cnt][j], f[last][j + Y[i]]);                if (j > L[i] && j < H[i] && f[cnt][j] != INF) farthest = i;            }        }        //处理不能到的地方        for (int j = 0; j <= L[i]; j++)        {            f[cnt][j] = INF;        }        for (int j = H[i]; j <= m; j++)        {            f[cnt][j] = INF;        }    }    INT ans = INF;    for (int i = 1; i <= m; i++)    {        if (f[n & 1][i] < ans)        {            ans = f[n & 1][i];        }    }    if (ans == INF)    {        cout << 0 << endl << nPipe[farthest] << endl;    }    else    {        cout << 1 << endl << ans << endl;    }}int main(){    run();    return 0;}

DP Orz

原创粉丝点击