[USACO 2017 Jan Gold] Tutorial

Link:

传送门

A:

按值大小插入后用树状数组统计两边个数

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
#define pb push_back
typedef double db;
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=1e5+10;
P dat[MAXN];
int n,bit[MAXN],dsp[MAXN],l[MAXN],r[MAXN],res,tot;

void Update(int x)
{while(x<=n) bit[x]++,x+=x&(-x);}
int Query(int x)
{
    int ret=0;
    while(x) ret+=bit[x],x-=x&(-x);
    return ret;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
        scanf("%d",&dat[i].X),dsp[++tot]=dat[i].X,dat[i].Y=i;
    sort(dsp+1,dsp+n+1);tot=unique(dsp+1,dsp+n+1)-dsp-1;
    for(int i=1;i<=n;i++)
        dat[i].X=lower_bound(dsp+1,dsp+tot+1,dat[i].X)-dsp;
    sort(dat+1,dat+n+1,greater<P>());
    
    for(int i=1;i<=n;i++)
    {
        l[i]=Query(dat[i].Y-1);
        r[i]=i-1-l[i];Update(dat[i].Y);
        if(max(l[i],r[i])>min(l[i],r[i])*2) res++;
    }
    printf("%d",res);
    return 0;
}
Problem A

B:

$dp[i][j][k]$表示前$i$项换$j$次末状态为$k$的最优解

每次分换或不换转移即可

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
#define pb push_back
typedef double db;
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=1e5+10;
char s[10];int res=0;
int n,m,dat[MAXN],dp[MAXN][25][5];

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        if(s[1]=='H') dat[i]=0;
        else if(s[1]=='P') dat[i]=1;
        else dat[i]=2;
    }
    dp[1][0][dat[1]]=1;
    for(int i=1;i<n;i++)
        for(int j=0;j<=m;j++)
            for(int k=0;k<3;k++)
            {
                dp[i+1][j][k]=max(dp[i+1][j][k],dp[i][j][k]+(k==dat[i+1]));
                for(int l=0;l<3;l++)
                    if(l!=k) dp[i+1][j+1][l]=max(dp[i+1][j+1][l],dp[i][j][k]+(l==dat[i+1]));
            }
    for(int i=0;i<=m;i++)
        for(int j=0;j<3;j++)
            res=max(res,dp[n][i][j]);
    printf("%d",res);
    return 0;
}
Problem B

C:

一开始看到数据范围$n<20$感觉就是暴力……

最后发现用一个6维数组记录当前两点位置和朝向$bfs$就行了……

注意:

1、一个点到终点后就不再移动了!

2、学会用函数返回引用来简化代码,这样就不用每次写6维的数了……

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
#define pb push_back
typedef double db;
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=25;
char s[MAXN];
struct node{int x1,y1,d1,x2,y2,d2;};
queue<node> q;int res=1<<30;
int n,dat[MAXN][MAXN],d[25][25][4][25][25][4];

int dx[]={-1,0,1,0};
int dy[]={0,1,0,-1};

int nxt(int x){return (x+1)%4;}
int pre(int x){return (x+3)%4;}
void update(node x,node y)
{
    int &a=d[x.x1][x.y1][x.d1][x.x2][x.y2][x.d2];
    int &b=d[y.x1][y.y1][y.d1][y.x2][y.y2][y.d2];
    if(a>b+1) a=b+1,q.push(x);
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        for(int j=1;j<=n;j++)
            dat[i][j]=(s[j]=='E');
    }
    memset(d,0x3f,sizeof(d));
    //注意一个点到终点后就不动了 
    d[n][1][0][n][1][1]=0;
    q.push((node){n,1,0,n,1,1});
    while(!q.empty())
    {
        node t=q.front();q.pop();
        node a=t,b=t,c=t;
        if(!(t.x1==1&&t.y1==n)) a.d1=nxt(a.d1),b.d1=pre(b.d1);
        if(!(t.x2==1&&t.y2==n)) a.d2=nxt(a.d2),b.d2=pre(b.d2);
        
        update(a,t);update(b,t);
        
        int fx1=c.x1+dx[c.d1],fx2=c.x2+dx[c.d2];
        int fy1=c.y1+dy[c.d1],fy2=c.y2+dy[c.d2];
        if(fx1>=1&&fy1<=n&&dat[fx1][fy1]&&!(c.x1==1&&c.y1==n)) c.x1=fx1,c.y1=fy1;
        if(fx2>=1&&fy2<=n&&dat[fx2][fy2]&&!(c.x2==1&&c.y2==n)) c.x2=fx2,c.y2=fy2;
        update(c,t);
    }
    
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++)
            res=min(res,d[1][n][i][1][n][j]);
    printf("%d",res);
    return 0;
}
Problem C
原文地址:https://www.cnblogs.com/newera/p/9637699.html