NOIP2010 机器翻译 乌龟棋 关押罪犯

NOIP2010

这里写图片描述
这里写图片描述
第一题
题目较简单,可以直接模拟,used【】存内存中的数,如果溢出就删除。

#include <iostream>
#include <cstdio>
using namespace std;

const int N = 1010;

int used[N], a[N];
int tot, b ,j;

int main(){
    freopen("translate.in","r",stdin);
    freopen("translate.out","w",stdout);
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i=1; i<=m; i++){
        j++;
        scanf("%d", &a[j]);
        if(used[a[j]] == 0){
            used[a[j]]++; tot++; b++;
            if(b > n) used[a[j-n]]--;
            if(used[a[j-n]] < 0) used[a[j-n]] = 0;//不允许减为负
        }
        else j--;
    }
    printf("%d", tot);
    return 0;
}

这里写图片描述
这里写图片描述
第二题
又是一道明显的dp题,但是针对这道题目特有的优化却不容易想到。关键就是要仔细分析数据范围(每种卡片的张数不超过四十!!!)这就是提示我们如何dp的最关键信息。因为四张卡片的用量一旦确定下来,乌龟所在的位置也就确定下来了。少一张几步的卡片就从几步之前的位置转移而来,状态转移的方程是很容易写的。所以四维的dp解决问题。

#include<iostream>
#include<cstdio>
#include <algorithm>
using namespace std;

int m,k,t;
int c[150],f[41][41][41][41],v[355],sum[5];

int main()
{
    freopen("tortoise.in","r",stdin);
    freopen("tortoise.out","w",stdout);
    scanf("%d%d",&t,&m);
    for(int i=1; i<=t; i++)
        scanf("%d",&v[i]);
    for(int i=1; i<=m; i++){
        scanf("%d",&c[i]);
        sum[c[i]]++;
    }
    f[0][0][0][0] = v[1];
    for(int i=0; i<=sum[1]; i++)
    for(int j=0; j<=sum[2]; j++)
    for(int k=0; k<=sum[3]; k++)
    for(int l=0; l<=sum[4]; l++){ 
        int s = i + 2*j + 3*k + 4*l + 1; 
        if(i > 0)
        f[i][j][k][l] = max(f[i][j][k][l], f[i-1][j][k][l] + v[s]);
        if(j > 0)
        f[i][j][k][l] = max(f[i][j][k][l], f[i][j-1][k][l] + v[s]);
        if(k > 0)
        f[i][j][k][l] = max(f[i][j][k][l], f[i][j][k-1][l] + v[s]);
        if(l > 0)
        f[i][j][k][l] = max(f[i][j][k][l], f[i][j][k][l-1] + v[s]);
    }   
    printf("%d",f[sum[1]][sum[2]][sum[3]][sum[4]]);
    return 0;
}

这里写图片描述
这里写图片描述
第三题
这是一道用并差集解决的题目,这里有一个最优的思想,就是要先处理权值高的遍,因为最终的结果由最大值决定,所以说如果大的遍不能解决,小的遍就没什么意义了。所以我们存完遍后,先排一次序,然后从大到小地模拟放点入栈的操作,直到产生矛盾,就取用最大值作为答案。还有就是如果在模拟的过程中遇见没法确定放入哪个栈的情况,就先不管它,打上一个标记,等有新的点被放入栈之后,再返回来判断。因为是在编的中途想到的并查集,下面的代码并不是标准的并查集,只是采用了并查集的思想。而且可能有一些瑕疵,仅供参考。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 100010;

int fam[20010], used[N];

struct ln{
    int sum;
    int p1, p2;
}ed[N];

bool mycmp(ln a,ln b){
    return a.sum > b.sum;
}

int main()
{
    freopen("prison.in","r",stdin);
    freopen("prison.out","w",stdout);
    memset(fam, -1, sizeof(fam));
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i=1; i<=m; i++){
        scanf("%d%d%d", &ed[i].p1, &ed[i].p2, &ed[i].sum);
    }
    sort(ed+1,ed+1+m,mycmp);
    fam[ed[1].p1] = 0; fam[ed[1].p2] = 1; used[1] = 1;
    int k = 2, nxt = 1, flag = 0;
    while(ed[k].sum){
        int u1 = ed[k].p1; 
        int u2 = ed[k].p2;
        if(fam[u1] == -1 && fam[u2] > -1 && used[k] == 0){
            fam[u1] = (fam[u2] + 1) % 2;
            used[k] = 1;
            k = nxt-1; 
            nxt = 1;
        }
        else if(fam[u2] == -1 && fam[u1] > -1 && used[k] == 0){
            fam[u2] = (fam[u1] + 1) % 2;
            used[k] = 1;
            k = nxt-1;
            nxt = 1;
        }
        else if(fam[u2] == -1 && fam[u1] == -1 && nxt == 1){
            nxt = k;
        }
        else if(fam[u2] > -1 && fam[u1] > -1 && used[k] == 0 && fam[u2] == fam[u1]){
            printf("%d", ed[k].sum);
            flag = 1;
            break;
        }
        k++;
    }
    if(flag == 0) printf("%d", 0);
    return 0;
}

这里写图片描述
这里写图片描述
第四题
用dfs实现本题是一个很容易想到的思路,但是有一个问题就是我们如何找出一个最优解呢?dfs可以帮助我们判断输出0 or 1(打标记),如果是1,我们就需要找最优解,那么简单的证明一下,由一个临水城市可以到达的沙漠城市一定是一条连续的线段,可以用反证法论证,如果不连续,这个断点就会由另一个临水城市到达,那么这两个临水城市覆盖的位置就一定会交叉,那么交叉点下面的城市(断点)就一定可以被这两个城市共同覆盖。于是就可以采用线段覆盖(类似dp)的方法快速求得最优。因为数据里有一个点会卡dfs,所以说经过反复尝试,用c提交+register不会T掉。

#include<string.h>
#include<stdio.h>
inline int readin(){
    static char ch;
    static int res;
    while((ch=getchar())>'9'||ch<'0');res=ch-48;
    while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;
    return res;
}
int n, m, num = 0;
int vis[510][510], ans[510] ,pos[510][510], f[510];
struct ln{
     int l,r;
}ed[510];

int min(int a, int b){
    return a<b?a:b;
}

int max(int a, int b){
    return a>b?a:b;
}

void dfs(int x,int y,int st)
{
     vis[x][y] = 1;
     if(x == m){
          ans[y] = 1;
          ed[st].l = min(ed[st].l, y);
          ed[st].r = max(ed[st].r, y);
     }
     if(pos[x+1][y] < pos[x][y] && x != m && !vis[x+1][y]) dfs(x+1,y,st);
     if(pos[x-1][y] < pos[x][y] && x != 1 && !vis[x-1][y]) dfs(x-1,y,st);
     if(pos[x][y+1] < pos[x][y] && y != n && !vis[x][y+1]) dfs(x,y+1,st);
     if(pos[x][y-1] < pos[x][y] && y != 1 && !vis[x][y-1]) dfs(x,y-1,st);
}

int main()
{
    freopen("flow.in","r",stdin);
    freopen("flow.out","w",stdout);
    m=readin(),n=readin();
    for(register int i=1; i<=n; i++)
        ed[i].l = f[i] = 10000000;
    for(register int i=1; i<=m; i++)
    for(register int j=1; j<=n; j++)
        pos[i][j]=readin();
    for(register int i=1; i<=n; i++){
        memset(vis, 0, sizeof(vis));
        dfs(1, i, i);
    }
    for(register int i=1; i<=n; i++)
      if(ans[i] == 0) num++;
    if(num > 0) {
        printf("%d\n%d", 0, num);
        return 0;
    }
    else{
        printf("1\n");
        for(register int i=1; i<=n; i++)
        for(register int j=1; j<=n; j++){
            if(i >= ed[j].l && i <= ed[j].r)
            f[i] = min(f[i], f[ed[j].l - 1] + 1);
        }
        printf("%d", f[n]);
    }
    return 0;
}

考试总结
一次10年noip真题练习。一般来说第一道题比较简单,这一次还没有摸清题目,所以说第一道题的耗时较长,原因就是把题目想的过于复杂,其实用常规方法10min就完全可以解决,这样就耽误了之后做题的时间。第二题是一个四维的dp,最开始想的是五维,没有想到简化的方法,这类题目其实难度并不大,但是需要想出巧一点的方法。第三题是一个并查集的题目,思想没有问题,不过代码不够规范,导致最后一个点没有调出来。最后一道是dp+区间覆盖,当时没有严格的证明区间的正确性,方法不太好想,其中有一个测试点要卡dfs,最后用c语言提交没有超时(不能用algorithm,加.h,不能在for中定义变量)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!感谢你的提问。根据你的要求,我将为你解答关于NOIP2017普及t3问题的内容。 在这道题目中,我们需要解决一个关于盘的问题。题目描述了一个n×m的盘,每个格子上有一个非负整数。开始时,小明站在左上角的格子上,他可以向右或向下移动一步,每次移动的代价为目标格子上的数值。我们需要找到一条从左上角到右下角的路径,使得移动的总代价最小。 解决这个问题的一个常见的方法是使用动态规划(Dynamic Programming)。我们可以创建一个二维数组dp,其中dp[i][j]表示从起点到达坐标为(i, j)的格子时的最小代价。然后,我们可以按照从左上角到右下角的顺序依次计算dp数组的值。 具体的计算方法如下: 1. 首先,我们可以初始化dp数组的第一行和第一列,即dp[0][j]和dp[i][0],它们表示从起点到达第一行和第一列的格子时的最小代价。初始化的方法是累加前面的格子的代价。 2. 接下来,我们可以使用一个双重循环,从(1, 1)开始遍历整个盘。对于每个格子(i, j),我们可以选择从上方格子(i-1, j)或左方格子(i, j-1)中选择一个代价较小的路径,并加上当前格子的代价。即dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]。 3. 最后,当我们计算完dp数组的所有值后,dp[n-1][m-1]即为从起点到达右下角的格子时的最小代价。 这样,我们就可以得到从左上角到右下角的最小代价。希望能对你的问题有所帮助!如果你还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值