NOIP2010提高组题解

版权声明:欢迎转载+原文章地址~ https://blog.csdn.net/Hi_KER/article/details/82110315

T1:机器翻译

考察知识:队列,模拟

算法难度:X+ 实现难度:X+

分析:真的很简单,直接按照题意模拟即可

代码:

#include<cstdio>
int n,m,que[1005],L,R,cnt=0,k;
int main(){
    scanf("%d%d",&m,&n);
    while(n--){
        bool find=false;
        scanf("%d",&k);
        for(int i=L;i<R;i++) if(que[i]==k) find=true;
        if(!find) cnt++,que[R++]=k;
        if(R-L>m) L++; 
    }
    printf("%d\n",cnt);
    return 0;
}

T2:乌龟棋

考察知识:动态规划

算法难度:XXX 实现难度:XX+

分析:如果想出了状态转移方程就简单了,但是想不出,那就......

注意到只有四种卡片

定义状态方程:f[i][j][k][l]表示分别用i,j,k,l张数字分别为1,2,3,4的卡片所能得到的最大分数

状态转移方程:f(i,j,k,l)=max\left\{\begin{matrix} f(i-1,j,k,l) & \\ f(i,j-1,k,l)& \\ f(i,j,k-1,l)& \\ f(i,j,k,l-1)& \end{matrix}\right.+p[i+j*2+k*3+l*4]

代码:

#include<cstdio>
#define F(var,L,R) for(int var=L;var<=R;var++)
#define Max(var,var_) var=var>(var_)?var:(var_)
int p[355],m,n,card[5],k,f[42][42][42][42];
int main(){
    scanf("%d%d",&n,&m);
    F(i,1,n) scanf("%d",p+i);
    F(i,1,m) scanf("%d",&k),card[k]++;
    F(i,0,card[1]) F(j,0,card[2])
    F(k,0,card[3]) F(l,0,card[4]){
        int pos=i+j*2+k*3+l*4+1;
        if(i) Max(f[i][j][k][l],f[i-1][j][k][l]);
        if(j) Max(f[i][j][k][l],f[i][j-1][k][l]);
        if(k) Max(f[i][j][k][l],f[i][j][k-1][l]);
        if(l) Max(f[i][j][k][l],f[i][j][k][l-1]);
        f[i][j][k][l]+=p[pos];
    }
    printf("%d\n",f[card[1]][card[2]][card[3]][card[4]]);
    return 0;
}

T3:关押罪犯

考察知识:二分图的判定,二分

算法难度:XXX 实现难度:XXX

分析:二分怨气值,假设怨气值小于等于mid,那么我们要把所有怨气值小于等于mid的边全部忽略,然后判断剩下的图是否为二分图

代码:

#include<cstdio>
#include<cstring>
const int maxn=200005;
struct edge{
    int to,next,w;
}e[maxn*10];
int head[maxn],np,c[maxn];
void add(int u,int v,int w){
    e[++np]=(edge){v,head[u],w};
    head[u]=np;
}
int n,m;
bool dfs(int i,int C,int limt){
    c[i]=C;
    for(int p=head[i];p;p=e[p].next) if(e[p].w>limt){
        int j=e[p].to;
        if(c[j]){
            if(c[j]==c[i]) return false;
        }
        else if(!dfs(j,-C,limt)) return false;
    }
    return true;
}
void build(){
    int u,v,w;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w),add(v,u,w);
    }
}
bool check(int limt){
    memset(c,0,sizeof(c));
    for(int i=1;i<=n;i++) if(!c[i]&&!dfs(i,1,limt))
        return false;
    return true;
}
void solve(){
    int l=0,r=1000000000,ans;
    while(l<=r){
        int mid=(l+r)/2;
        if(check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
}
int main(){
    build();
    solve();
    return 0;
}

T4:引水入城

考察知识:搜索,bfs,区间覆盖,贪心

算法难度:XXXX 实现难度:XXX+

分析:我们首先bfs判断干旱区是否满足全部可以建水利设施,如果可以,

我们枚举第一行的每一个格子i,可以证明:只在i建蓄水厂,那么干旱区可以建水利设施的地方一定是一段连续区间

如果你想到这一点了,那么基本上就解决这道题了,剩下的就是转化为区间覆盖问题然后解决,我们常常用贪心算法解决

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
struct line{
    int l,r;
    line(){l=r=0;}
    friend bool operator < (line A,line B){
        return A.l<B.l;
    }
}l[505];
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
int n,m,a[505][505];
bool vis[505][505],useless[505];
void bfs(int x,int y){
    queue<int>q;
    vis[x][y]=true;
    int nx,ny;
    q.push(x),q.push(y);
    while(!q.empty()){
        x=q.front(),q.pop();
        y=q.front(),q.pop();
        for(int i=0;i<4;i++){
            nx=x+dx[i],ny=y+dy[i];
            if(nx<1||nx>n||ny<1||ny>m||a[nx][ny]>=a[x][y]
            ||vis[nx][ny]) continue;
            vis[nx][ny]=true;
            q.push(nx),q.push(ny);
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
        scanf("%d",&a[i][j]);
    for(int i=1;i<=m;i++) bfs(1,i);
    int cnt=0;
    for(int i=1;i<=m;i++) if(!vis[n][i]) cnt++;
    if(cnt){printf("0\n%d",cnt);return 0;}//判断是否满足要求 
    for(int i=1;i<=m;i++){
        memset(vis,0,sizeof(vis));
        bfs(1,i);
        for(int k=1;k<=m;k++) if(vis[n][k]){//生成区间 
            l[i].l=k;break;
        }
        for(int k=m;k>0;k--) if(vis[n][k]){
            l[i].r=k;break;
        }
    }
    sort(l+1,l+m+1);
    int L=1,k=1;
    while(k<=m&&L<=m){//区间覆盖问题 
        int tmp=-1;
        while(k<=m&&l[k].l<=L){
            if(l[k].r>tmp) tmp=l[k].r;
            k++;
        }
        cnt++,L=tmp+1;
    }
    printf("1\n%d",cnt);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Hi_KER/article/details/82110315