1. 程式人生 > >NOIP2010題解

NOIP2010題解

NOIP2010題解

顯然原來都寫過,都重新寫一遍。

機器翻譯 translate

一道很容易的模擬題,直接使用一個佇列維護一下順序就好了。

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
bool vis[1050];
int Q[1050],h,t,ans,n,m;
int main()
{
    m=read();n=read();h=1;t=0;
    for(int i=1;i<=n;++i)
    {
        int x=read();if(vis[x])continue;
        Q[++t]=x;++ans;vis[x]=true;
        if(t-h+1>m)vis[Q[h++]]=false;
    }
    printf("%d\n",ans);
    return 0;
}

烏龜棋 tortoise

一個不難想的\(dp\)是設\(f[i][a1][a2][a3][a4]\)表示當前在\(i\)位置,四種卡牌分別用的張數為\(a1,a2,a3,a4\)時的最大收益。不難發現\(i\)可以通過後面四個數算出來,顯然是一個多餘狀態,去掉後直接\(dp\)即可。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 400
void cmax(int &x,int y){if(x<y)x=y;}
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,m;
int a[MAX],b[5];
int f[45][45][45][45];
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)a[i]=read();
    for(int i=1;i<=m;++i)b[read()]+=1;
    memset(f,-63,sizeof(f));
    f[0][0][0][0]=a[1];
    for(int i=0;i<=b[1];++i)
        for(int j=0;j<=b[2];++j)
            for(int k=0;k<=b[3];++k)
                for(int l=0;l<=b[4];++l)
                {
                    int s=1*i+2*j+3*k+4*l+1;
                    if(i)cmax(f[i][j][k][l],f[i-1][j][k][l]+a[s]);
                    if(j)cmax(f[i][j][k][l],f[i][j-1][k][l]+a[s]);
                    if(k)cmax(f[i][j][k][l],f[i][j][k-1][l]+a[s]);
                    if(l)cmax(f[i][j][k][l],f[i][j][k][l-1]+a[s]);
                }
    printf("%d\n",f[b[1]][b[2]][b[3]][b[4]]);
    return 0;
}

關押罪犯 prison

很妙的題目,如果沒有做過類似題目的話自己基本不可能做出來。

不難想到的一點是這樣的,我們把所有限制按照權值從大往小排序,因為要最大值最小,所以顯然竟可能的滿足權值較大的限制。但是我們發現限制是\(x,y\)不能在一個集合。那麼對於每一個點拆成兩個,一個表示和它在一個集合,一個表示和它不在一個集合。對於一個限制,如果\(x,y\)已經在一個集合內,顯然這個限制的權值就是答案了。否則合併\(x\)\(y\)的集合關係,即把\(x\)合併到和\(y\)不在一個集合內的集合中去,\(y\)同理。並查集實現即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 40040
#define MAXL 100100
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,m;
struct Line{int u,v,w;}e[MAXL];
bool operator<(Line a,Line b){return a.w>b.w;}
int f[MAX];
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;++i)e[i].u=read(),e[i].v=read(),e[i].w=read();
    sort(&e[1],&e[m+1]);
    for(int i=1;i<=n+n;++i)f[i]=i;
    for(int i=1;i<=m;++i)
    {
        int u=e[i].u,v=e[i].v;
        if(getf(u)==getf(v))
        {
            printf("%d\n",e[i].w);
            return 0;
        }
        f[getf(u)]=getf(v+n);
        f[getf(v)]=getf(u+n);
    }
    printf("0\n");
    return 0;
}

引水入城 flow

也是一道很不錯的題目。

對於判定性問題,不用多想,在每一個位置都建立蓄水場,\(bfs\)一遍判定是否能夠滿足所有位置。如果不行就直接輸出。否則考慮如何計算最小值。

繼續觀察,發現當可以滿足所有位置的時候,在每個位置建立蓄水場,那麼它影響的範圍一定是一段連續區間。證明不難。如果不是一段區間的話,假設\(x\)為其中的一個分割位置,因為有解,所以\(x\)必定能被\(x-1,x+1\)\(x\)上一行這三個格子中的一個影響到,而\(x\)割開了一個線段,那麼必定跨越\(x\)這一列,所以不可能不影響到\(x\)位置。所以必定是一段連續區間。

那麼問題轉化成給定若干線段,選出最少的線段覆蓋所有區間,貪心即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
#define MAX 505
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int d[4][2]={1,0,-1,0,0,1,0,-1};
int a[MAX][MAX];
int n,m,tot,ans;
struct effect{int l,r;}p[MAX];
bool operator<(effect a,effect b){if(a.l!=b.l)return a.l<b.l;return a.r>b.r;}
bool vis[MAX][MAX];
namespace check
{
    queue<int> Qx,Qy;
    void bfs()
    {
        for(int i=1;i<=m;++i)Qx.push(1),Qy.push(i),vis[1][i]=true;
        while(!Qx.empty())
        {
            int x=Qx.front(),y=Qy.front();Qx.pop(),Qy.pop();
            for(int i=0;i<4;++i)
            {
                int xx=x+d[i][0],yy=y+d[i][1];
                if(xx<1||yy<1||xx>n||yy>m)continue;
                if(vis[xx][yy])continue;
                if(a[xx][yy]>=a[x][y])continue;
                vis[xx][yy]=true;
                Qx.push(xx);Qy.push(yy);
            }
        }
    }
    void work()
    {
        bfs();int sum=0;
        for(int i=1;i<=m;++i)
            if(!vis[n][i])++sum;
        if(sum){printf("0\n%d\n",sum);exit(0);}
        puts("1");
    }
}
namespace Get
{
    void bfs(int Sx,int Sy)
    {
        memset(vis,0,sizeof(vis));queue<int> Qx,Qy;
        Qx.push(Sx);Qy.push(Sy);vis[Sx][Sy]=true;
        while(!Qx.empty())
        {
            int x=Qx.front(),y=Qy.front();Qx.pop(),Qy.pop();
            for(int i=0;i<4;++i)
            {
                int xx=x+d[i][0],yy=y+d[i][1];
                if(xx<1||yy<1||xx>n||yy>m)continue;
                if(vis[xx][yy])continue;
                if(a[xx][yy]>=a[x][y])continue;
                vis[xx][yy]=true;
                Qx.push(xx);Qy.push(yy);
            }
        }
    }
    void work()
    {
        for(int i=1;i<=m;++i)
        {
            bfs(1,i);int mx=0,mn=1e9;
            for(int j=1;j<=m;++j)
                if(vis[n][j])mx=j,mn=mn<j?mn:j;
            if(mx<mn)continue;
            p[++tot]=(effect){mn,mx};
        }
    }
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            a[i][j]=read();
    check::work();Get::work();
    sort(&p[1],&p[tot+1]);
    int sum=0,r=0;
    for(int i=1;i<=tot;++i)
        if(p[i].r>p[i-1].r)p[++sum]=p[i];
    tot=sum;
    for(int i=1,j;i<=tot;i=j)
    {
        int nr=r;j=i;
        while(j<=tot&&p[j].l<=r+1)nr=max(nr,p[j++].r);
        ans+=1;r=nr;
    }
    printf("%d\n",ans);
    return 0;
}