2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div.1)

A. Alphabet

题意:给出一个字符串,问最少添加多少个字母可以出现子串a-z。

题解:最长上升子序列。答案就是26减去LIS的长度。

#include<bits/stdc++.h>
#define maxn 200050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,dp[maxn];
char s[maxn];

int main()
{
    scanf("%s",s);
    n=strlen(s);
    memset(dp,0,sizeof(dp));
    for(int i=0;i<n;i++)
        dp[i]=1;
    for(int i=0;i<n;i++)
    {
        for(int j=i+1;j<n;j++)
        {
            if(s[j]>s[i])
                dp[j]=max(dp[j],dp[i]+1);
        }
    }
    int ans=0;
    for(int i=0;i<n;i++)
        ans=max(ans,dp[i]);
    printf("%d\n",26-ans);
    return 0;
}

B. Buggy Robot

题意:一个机器人要在迷宫里从起点走到终点,现给出了一系列指令,但机器人按照这些指令行走有可能不能到达终点。问最少需要增加/删去几条指令可以令机器人走到终点。

题解:有点奇特的dp,用dp[i][j][k]表示从第k条指令走到(i,j)所需要修改的最小指令数。dp[i][j][k]可以从前一条指令直接执行过来,也可以从dp[i][xx][yy]添加或删除若干条指令后转移过来。枚举所有可能的状态,加入bfs中求解。

#include<bits/stdc++.h>
#define maxn 55
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,m,sx,sy,ex,ey,len;
char maze[maxn][maxn],s[maxn];
int dp[maxn][maxn][maxn];
int pos[4][2]={1,0,0,1,-1,0,0,-1};
map<char,int>mo;
struct node
{
    int x,y;
    int num;
    node(int x=0,int y=0,int num=0):x(x),y(y),num(num){}
};

bool judge(int x,int y)
{
    if(x>0&&x<=n&&y>0&&y<=m&&maze[x][y]!='#')
        return true;
    return false;
}

void init()
{
    mo['D']=0,mo['R']=1;
    mo['U']=2,mo['L']=3;
    for(int i=0;i<maxn;i++)
    {
        for(int j=0;j<maxn;j++)
        {
            for(int k=0;k<maxn;k++)
                dp[i][j][k]=INF;
        }
    }
}

void bfs()
{
    queue<node>q;
    q.push(node(sx,sy,0));
    while(!q.empty())
    {
        node now=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int fx=now.x+pos[i][0];
            int fy=now.y+pos[i][1];
            if(!judge(fx,fy))continue;
            if(dp[fx][fy][now.num]>dp[now.x][now.y][now.num]+1)
            {
                dp[fx][fy][now.num]=dp[now.x][now.y][now.num]+1;
                q.push(node(fx,fy,now.num));
            }
        }
        if(now.num<len)
        {
            if(dp[now.x][now.y][now.num+1]>dp[now.x][now.y][now.num]+1)
            {
                dp[now.x][now.y][now.num+1]=dp[now.x][now.y][now.num]+1;
                q.push(node(now.x,now.y,now.num+1));
            }
            int tmp=mo[s[now.num+1]];
            int fx=now.x+pos[tmp][0];
            int fy=now.y+pos[tmp][1];
            if(!judge(fx,fy))fx=now.x,fy=now.y;
            if(dp[fx][fy][now.num+1]>dp[now.x][now.y][now.num])
            {
                dp[fx][fy][now.num+1]=dp[now.x][now.y][now.num];
                q.push(node(fx,fy,now.num+1));
            }
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    init();
    for(int i=1;i<=n;i++)
    {
        scanf("%s",maze[i]+1);
        for(int j=1;j<=m;j++)
        {
            if(maze[i][j]=='R')
                sx=i,sy=j;
            else if(maze[i][j]=='E')
                ex=i,ey=j;
        }
    }
    scanf("%s",s+1);
    len=strlen(s+1);
    dp[sx][sy][0]=0;
    bfs();
    int ans=INF;
    for(int i=0;i<=len;i++)
        ans=min(ans,dp[ex][ey][i]);
    printf("%d\n",ans);
    return 0;
}

C. Cameras

题意:直线上一次有n个点,某一些点已经被标记。问最少再标记多少个点才能使任意连续的r个点中都至少有2个被标记。

题解:先保证最前面的r个,这样对于后面在移动区间的时候,只需要贪心地看第一个和最后一个有没有标记即可。时间复杂度O(N)。

#include<bits/stdc++.h>
#define maxn 200050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,k,r,a;
bool flag[maxn];

int main()
{
    scanf("%d%d%d",&n,&k,&r);
    memset(flag,0,sizeof(flag));
    for(int i=0;i<k;i++)
    {
        scanf("%d",&a);
        flag[a]=1;
    }
    int num=0,ans=0;
    for(int i=1;i<=r;i++)
    {
        if(flag[i])
            num++;
    }
    for(int i=r;i>=1;i--)
    {
        if(num>=2)break;
        if(!flag[i])
        {
            flag[i]=1;
            ans++,num++;
        }
    }
    for(int i=2;i+r-1<=n;i++)
    {
        if(flag[i-1])num--;
        if(flag[i+r-1])num++;
        if(num<2)
        {
            for(int j=(i+r-1);j>=i;j--)
            {
                if(!flag[j])
                {
                    flag[j]=1;
                    ans++,num++;
                    if(num>=2)break;
                }
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

G. Maximum Islands

题意:在地图上有陆地和水域,还有一些不确定区域可能是陆地也可能是水域。问最多可能有多少个陆地连通块。

题解:为了保证连通块数目最大, 我们尽量使连通块的大小为1,首先将所有与已知的陆地相连的未知区域全部标记为水。考虑到不确定区域的陆地一定与水相邻,于是在所有相邻的未知区域之间连边,建立一个新图,答案就是原有的陆地数目加上新图的最大独立集。

#include<bits/stdc++.h>
#define maxn 2500
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,m,id;
char g[maxn][maxn];
bool vis[maxn],flag[maxn][maxn];
int linker[maxn];
int pos[4][2]={1,0,0,1,-1,0,0,-1};
map< pair<int,int>,int >mo;
vector<int>v[maxn];

bool judge(int x,int y)
{
    if(x>=0&&y>=0&&x<n&&y<m)
        return true;
    return false;
}

void pdfs(int x,int y)
{
    flag[x][y]=1;
    for(int i=0;i<4;i++)
    {
        int fx=x+pos[i][0];
        int fy=y+pos[i][1];
        if(judge(fx,fy)&&!flag[fx][fy])
        {
            if(g[fx][fy]=='C')g[fx][fy]='W';
            else if(g[fx][fy]=='L')pdfs(fx,fy);
        }
    }
}

bool dfs(int u)
{
    vis[u]=1;
    int len=v[u].size();
    for(int i=0;i<len;i++)
    {
        int ve=v[u][i];
        if(!vis[ve])
        {
            vis[ve]=1;
            if(linker[ve]==-1||dfs(linker[ve]))
            {
                linker[ve]=u;
                linker[u]=ve;
                return true;
            }
        }
    }
    return false;
}

int hungary()
{
    int res=0;
    memset(linker,-1,sizeof(linker));
    for(int u=1;u<id;u++)
    {
        if(linker[u]<0)
        {
            memset(vis,0,sizeof(vis));
            if(dfs(u))
                res++;
        }
    }
    return res;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
    {
        scanf("%s",g[i]);
    }
    int ans=0;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(!flag[i][j]&&g[i][j]=='L')
            {
                pdfs(i,j);
                ans++;
            }
        }
    }
    id=1;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(g[i][j]=='C')
                mo[make_pair(i,j)]=id++;
        }
    }
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(g[i][j]!='C')continue;
            for(int k=0;k<4;k++)
            {
                int fx=i+pos[k][0];
                int fy=j+pos[k][1];
                if(g[fx][fy]!='C')continue;
                int a=mo[make_pair(i,j)];
                int b=mo[make_pair(fx,fy)];
                v[a].push_back(b);
                v[b].push_back(a);
            }
        }
    }
    printf("%d\n",ans+id-hungary()-1);
    return 0;
}

H. Paint

题意:给出一些区间,求这些区间两两不相交所能覆盖区域的最大值。

题解:先排序,排序时先按照右端点升序,再按照左端点升序。然后就是一个线性结构上的DP。

#include<bits/stdc++.h>
#define maxn 200050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int k;
ll dp[maxn],n;
struct node
{
    ll l,r;
    bool friend operator < (const node a,const node b)
    {
        if(a.r==b.r)return a.l>b.l;
        return a.r<b.r;
    }
}e[maxn];
bool cmp(node a,node b)
{
    return a.r<b.l;
}
ll Max(ll a,ll b){return a>b?a:b;}

int main()
{
    scanf("%I64d%d",&n,&k);
    for(int i=1;i<=k;i++)
    {
        scanf("%I64d%I64d",&e[i].l,&e[i].r);
    }
    sort(e+1,e+k+1);
    for(int i=1;i<=k;i++)
    {
        int tmp=lower_bound(e+1,e+k+1,e[i],cmp)-e-1;
        dp[i]=Max(dp[i-1],dp[tmp]+e[i].r-e[i].l+1);
    }
    printf("%I64d\n",n-dp[k]);
    return 0;
}

I. Postman

题意:数轴上有一些点,每个点有若干信件需要派送。邮递员在原点处,每次只能携带一定数目的信件。问邮递员所需要走的最小的距离。

题解:将正半轴与负半轴分开处理,贪心地送信件,每次都优先送最远的一个点。

#include<bits/stdc++.h>
#define maxn 2050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n;
ll k,xa;
struct node
{
    bool flag;
    ll x,m;
    bool friend operator < (const node a,const node b)
    {
        return a.x < b.x;
    }
}e[maxn];

int main()
{
    scanf("%d%I64d",&n,&k);
    for(int i=0;i<n;i++)
    {
        scanf("%I64d%I64d",&xa,&e[i].m);
        if(xa<0)
        {
            e[i].flag=1;
            e[i].x=-xa;
        }
        else
        {
            e[i].flag=0;
            e[i].x=xa;
        }
    }
    priority_queue<node>q;
    while(!q.empty())q.pop();
    for(int i=0;i<n;i++)
    {
        if(e[i].flag==0)
            q.push(e[i]);
    }
    ll ans=0;
    while(!q.empty())
    {
        node now=q.top();
        q.pop();
        ll num=(now.m+k-1)/k;
        ll cnt=num*k-now.m;
        ans+=(2LL*num*now.x);
        while(cnt&&!q.empty())
        {
            node temp=q.top();
            q.pop();
            if(temp.m<=cnt)cnt-=temp.m;
            else
            {
                temp.m-=cnt;
                cnt=0;
                q.push(temp);
            }
        }
    }
    while(!q.empty())q.pop();
    for(int i=0;i<n;i++)
    {
        if(e[i].flag==1)
            q.push(e[i]);
    }
    while(!q.empty())
    {
        node now=q.top();
        q.pop();
        ll num=(now.m+k-1)/k;
        ll cnt=num*k-now.m;
        ans+=(2*num*now.x);
        while(cnt&&!q.empty())
        {
            node temp=q.top();
            q.pop();
            if(temp.m<=cnt)cnt-=temp.m;
            else
            {
                temp.m-=cnt;
                cnt=0;
                q.push(temp);
            }
        }
    }
    printf("%I64d\n",ans);
    return 0;
}

J. Shopping

题意:有一些商品和一些顾客,每位顾客都只会在某一个连续的商品区间内购买,并且会依次购买所能购买的最大数目商品。商品数目无限,求每位顾客最终剩下的钱数。

题解:考虑到取模的性质,初始时有x元则最多只会购买log(x)次。预处理出区间最小值,每次购买的时候都二分地找出当前购买区间内第一个可以购买的商品并取模。

#include<bits.stdc++.h>
#define maxn 200050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,q,l,r;
ll a[maxn],v;
ll dp[maxn][23];
ll Min(ll a,ll b){return a<b?a:b;}

void init()
{
    for(int i=0;i<n;i++)
        dp[i][0]=a[i];
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=0;i+(1<<j)-1<n;i++)
            dp[i][j]=Min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
    }
}

ll query(int l,int r)
{
    int k=0;
    while((1<<(k+1))<=(r-l+1))k++;
    return Min(dp[l][k],dp[r-(1<<k)+1][k]);
}

ll solve(ll v,int l,int r)
{
    while(v)
    {
        int lx=l,lr=r;
        if(query(lx,lr)>v)return v;
        while(lx<lr)
        {
            int mid=(lx+lr)/2;
            if(query(lx,mid)<=v)lr=mid;
            else lx=mid+1;
        }
        v%=a[lr];
        l=lr+1;
        if(l>r)return v;
    }
    return v;
}

int main()
{
    scanf("%d%d",&n,&q);
    for(int i=0;i<n;i++)
    {
        scanf("%I64d",&a[i]);
    }
    init();
    for(int i=1;i<=q;i++)
    {
        scanf("%I64d%d%d",&v,&l,&r);
        l--,r--;
        printf("%I64d\n",solve(v,l,r));
    }
    return 0;
}

K. Tournament Wins

题意:有2^k人淘汰赛,你的能力排第r,问你能够赢下的比赛的场数的期望是多少。

题解:F(i)为至少i场的概率,G(i)为只赢i场的概率, 则Ans=G(1)+2G(2)+...+KG(K)=F(1)+F(2)+...F(K)

推导可得F(i)=C(2^i-1,2^k-r)/C(2^i-1,2^k-1)。

#include<bits/stdc++.h>
#define maxn 1200000
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int k,r,n;
double pre[maxn];

void init()
{
    pre[1]=0;
    for(int i=2;i<=(1<<k);i++)
        pre[i]=pre[i-1]+log(1.0*i);
}

int main()
{
    scanf("%d%d",&k,&r);
    init();
    double ans=0;
    n=(1<<k);
    for(int i=1;i<=k;i++)
    {
        int m=(1<<i);
        if(n-r<m-1)break;
        ans+=exp(pre[n-r]+pre[n-m]-pre[n-m-r+1]-pre[n-1]);
    }
    printf("%.5lf\n",ans);
    return 0;
}

L. Windy Path

题意:给出n个点和n-2个操作,求这些点的一个排列,使得如果第i个操作是L,i,i+1,i+2点必须逆时针,否则顺时针。

题解:一定有解。首先我们选定左下的点为第一个点。它在点集凸包上有两个相邻的点。把凸包内作为点的朝向,如果当前指令要求是L即逆时针的话,下一个点是当前点的当前凸包右相邻点。反之是左相邻点。每次要走的点都是当前未走过的点。

#include<bits/stdc++.h>
#define maxn 105
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;

int n,ans[maxn];
char s[maxn];
struct node
{
    int x,y;
    node(int x=0,int y=0):x(x),y(y){}
}e[maxn];
bool operator < (node a,node b)
{
    if(a.x==b.x)return a.y<b.y;
    return a.x<b.x;
}
node operator - (node a,node b){return node(a.x-b.x,a.y-b.y);}
int operator * (node a,node b){return a.x*b.y-a.y*b.x;}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&e[i].x,&e[i].y);
        ans[i]=i;
    }
    int tmp=1;
    for(int i=2;i<=n;i++)
    {
        if(e[i]<e[tmp])
            tmp=i;
    }
    swap(e[1],e[tmp]);
    swap(ans[1],ans[tmp]);
    scanf("%s",s+2);
    for(int i=2;i<n;i++)
    {
        int k=i,cnt=(s[i]=='L'?1:-1);
        for(int j=i+1;j<=n;j++)
        {
            if((e[k]-e[i-1])*(e[j]-e[i-1])*cnt<0)
                k=j;
        }
        swap(e[i],e[k]);
        swap(ans[i],ans[k]);
    }
    for(int i=1;i<=n;i++)
        printf(i==n?"%d\n":"%d ",ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/NPU_SXY/article/details/80168645