<队内胡策> 2017.10.24 求逆序对+表达式计算+贪心+Bfs+最短路、exgcd

5 篇文章 0 订阅
4 篇文章 0 订阅

*PS:为什么我t5死活都调不出来呢(눈‸눈)

T1:原创,参考noip火柴排队。
T2: codevs 2178 表达式运算Cuties (我自己找的)
T3:Codeforce867E(865D) Buy Low Sell High
T4: USACO 2015 DEC Gold  不明链接1   不明链接2
( 该网站不提供在线测评,仅提供数据下载。考试使用原数据。)
T5:原创。


这里写图片描述
这里写图片描述


这里写图片描述


T1 原创 火柴排队的改编版

一说到火柴排队,应该就会做了。(好巧 这个题我前些天才看过qwq)。

解法:
对于B中的每个字符都统计下他在A中出现的位置,存入一个数组里,这个位置就是它最后要移到的位置。最后对这个数组求一个逆序对,逆序对的数量就是最少需移动的步数。
不能的情况就是B和A的字符种类或数量不同,输入时处理。

代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;

int N,ans,cnt,Cnt;
int a[110],used[1000],b[110];
char c[110];
struct maple{
    int pos;
    char a;
}A[110],B[110];

bool cmp(maple c,maple d)
{
    return c.a<d.a;
}

void hebing(int l,int mid,int r)
{
    int i=l,j=mid+1,k=l-1;
    while(i<=mid&&j<=r)
    {
        if(a[i]<=a[j]) b[++k]=a[i],++i;
        else b[++k]=a[j],ans+=mid-i+1,++j;
    }
    while(i<=mid) b[++k]=a[i],++i;
    while(j<=r)  b[++k]=a[j],++j;
    for(int i=l;i<=r;++i)
      a[i]=b[i];
}
void Mergesort(int l,int r)
{
    if(l==r) return ;
    int mid=(l+r)>>1;
    Mergesort(l,mid);
    Mergesort(mid+1,r);
    hebing(l,mid,r);
} 
int main()
{
    freopen("order.in","r",stdin);
    freopen("order.out","w",stdout);
    scanf("%d",&N);
    scanf("%s",c);
    for(int i=0;i<N;++i)
    {
        A[i].a=c[i],A[i].pos=i;
        if(!used[c[i]]) ++cnt;
        ++used[c[i]];
    }
    scanf("%s",c);
    for(int j=0;j<N;++j)
    {
        B[j].a=c[j],B[j].pos=j;
        --used[c[j]];
        if(!used[c[j]]) ++Cnt;
        if(used[c[j]]<0) Cnt=-110;
    }
    if(cnt==Cnt)
    {
        sort(A,A+N,cmp);
        sort(B,B+N,cmp);
        for(int i=0;i<N;++i)
            a[B[i].pos]=A[i].pos;
        Mergesort(0,N-1);
        cout<<ans;
    }
    else cout<<-1;
    return 0;
}

这里写图片描述


T2 简单的栈应用

注意处理负数的情况,判断栈空的函数一定要放在最前面。

代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;

int k[100],f=1;
char a[210];
stack<long long> num;
stack<char> opt;

void Done()
{
    char x=opt.top();
    opt.pop();
    long long a=num.top();num.pop();
    long long b=num.top();num.pop();
    long long c=0;
    if(x=='+') c=a+b;
    if(x=='-') c=b-a;
    if(x=='*') c=b*a;
    if(x=='/') c=b/a;
    if(x=='^') 
    {
      c=1;
      while(a)
      {
          c*=b;
          --a;
      }
    }
    num.push(c);
}
int main()
{
    freopen("formula.in","r",stdin);
    freopen("formula.out","w",stdout);
    scanf("%s",a);
    k['+']=k['-']=1;
    k['*']=k['/']=2;
    k['^']=3;
    k['(']=4;
    num.push(0);
    long long l=strlen(a),Num=0;
    a[l]=')';
    for(int i=0;i<=l;++i)
    {
        if(a[i]>='0'&&a[i]<='9') Num=Num*10+(a[i]-'0'),Num*=f,f=1;
        else{
               if(Num!=0) num.push(Num); 
               Num=0;
               if(a[i]=='-'&&a[i-1]=='(') 
               {
                  f=-1;
                  continue;
               }
               if(a[i]==')')
               {
                   while(!opt.empty()&&opt.top()!='(')   Done();
                   if(!opt.empty()) opt.pop();
               }
               else if(opt.empty()||opt.top()=='('||k[a[i]]>k[opt.top()])      opt.push(a[i]);
               else 
               {
                   while(!opt.empty()&&opt.top()!='('&&k[a[i]]<=k[opt.top()]) Done();
                   opt.push(a[i]);
               }
        }
    }
    while(!opt.empty()&&opt.top()!='(') Done();
    cout<<num.top();
    return 0; 
}

这里写图片描述
这里写图片描述


T3 看似DP的贪心

是不是很像之前的一道有N天每天可买可卖可不作处理的dp问题??

但是那个题有个限制就是每天手里最多持有一份股票,而这个题则是多份的。

因为这个题是t3而之前又做过类似的题,所以我马上就在想dp的路上一去不复返了QAQ。。。

最后确实打出来了,测的时候却显示MLE!!!因为long long的数组开的太大!!! 好吧。。。最后知道long long 在128MB下也就能开个1600万+。。。

定义 f[i][j]表示到第i天手里有j份股票所赚到的最多的钱数

可知

   f[i][j]=max(f[i][j],f[i-1][j]);         // 不作处理
   f[i][j]=max(f[i][j],f[i-1][j+1]+P[i]);  // 卖出
   f[i][j]=max(f[i][j],f[i-1][j-1]-P[i]);  //买入

改完后可以过5个点

DP代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
using namespace std;
const int Cn=300000+10;

int N;
long long P[Cn],f[4000][4000];

int main()
{
    freopen("resell.in","r",stdin);
    freopen("resell.out","w",stdout);
    scanf("%d",&N);
    for(int i=1;i<=N;++i) 
       scanf("%lld",&P[i]);
    for(int i=1;i<=N;++i)
       for(int j=0;j<=N;++j)
       {
          f[i][j]=-1e9+7;
          if(j<=i) 
          {
               if(i!=1)
               {
                 f[i][j]=max(f[i][j],f[i-1][j]);
                 f[i][j]=max(f[i][j],f[i-1][j+1]+P[i]);
                 if(j-1>=0) f[i][j]=max(f[i][j],f[i-1][j-1]-P[i]);
               } 
               else f[1][0]=0,f[1][1]=-P[1];
          }
       }
    cout<<f[N][0];
    return 0;
}

emmmm。。。如果用滚动数组优化的话可以过6个点,就是另开一个数组先记录一下f[i-1]的值用来更新。。。这里就不给出代码了。

正解 ·贪心

来自loi.Sherlock的题解总结(就是讲题时我做的笔记啊qwq,略长)

emmm...要赚的钱最多,我们肯定是低价买入,高价买出。。

大体上想想:可以维护一个小根堆,每过一天,用当天的价格和堆顶比较,如果价格大于堆顶,ans就加上*当天价格-堆顶价格*,弹出堆顶;如果小于等于,就加入堆。

但是这个做法有一个bug,举个栗子:

2 5 10 

按照我们现在的贪心做法,答案应该是 5-2=3 (每天只能操作一次),但是正确答案应该是 10-2=8

这就很尴尬了。。。(划掉)

这时我们发现 10-2=(10-5)+(5-2) ,这是不是代表我可以利用一下5呢??

对于 a<b<c 的情况,我要利用中介b ,就把b放一个进去(之前是b>a时,只统计ans,b并没有被放入)用来做中介。

但是这样还有一个bug 

如 2 5 10 7   输出 10-5+5-2=8 ,但正确答案是 10-2+7-5=10

我们发现之前的5实际上并没有被用到,反而被弹出了。

解决的方法就是一开始就放上两个5,一个做中介,一个用来更新答案。

所以对之前贪心做法的改进就是当我遇到一个比堆顶大的元素时,更新ans,弹出堆顶,同时放入两个当前价格,对答案不会有影响。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;

int N;
long long ans=0;
int P[300010];

priority_queue<int,vector<int>,greater<int> >q;

int main()
{
    freopen("resell.in","r",stdin);
    freopen("resell.out","w",stdout);
    scanf("%d",&N);
    for(int i=1;i<=N;++i)
    {
        scanf("%d",&P[i]);
        if(!q.empty()&&P[i]>q.top())
        {
            ans+=P[i]-q.top();
            q.pop();
            q.push(P[i]);
        }
        q.push(P[i]);
    }
    printf("%lld",ans);
    return 0;
}

这里写图片描述
这里写图片描述


T4 无比麻烦的BFS

注意特判啊!!我做的时候WA,TLE,MLE真是够了!

注意紫色的格子上的移动方向是与之前进入的方向有关的,并不是可以随便走的!! 所以要另开一维记录前进的方向,但是并不是每个格子的前进方向都要记录,只需记录紫色格子的,不然会TLE。。

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int mxn=1000+5;

int N,M,f;
int x[4]={0,1,0,-1},  //  0--> 右 1--> 下 2--> 左 3-->上 
    y[4]={1,0,-1,0};  
int A[mxn][mxn];
int used[mxn][mxn][2][5];
struct maple{
    int x,y,z,face,d;
};
queue<maple>q;  // 手打队列会mle 

bool can(int k,maple a,int K)
{
    int X=a.x+x[k],Y=a.y+y[k];
    if(X<=0||X>N||Y<=0||Y>M||used[X][Y][a.z][K]) return false;
    if(A[X][Y]==0) return false;    
    if(A[X][Y]==3&&!a.z) return false;
    return true;
}
void Bfs()
{
    q.push((maple){1,1,0,0,0});
    used[1][1][0][4]=1;
    while(!q.empty())
    {
        maple a=q.front();
        q.pop();
        if(a.x==N&&a.y==M)  
        {
             f=1;
             printf("%d",a.d);
             return ;
        }
        if(A[a.x][a.y]==4)
        {
           int face=a.face;
           int X=a.x+x[a.face],Y=a.y+y[a.face];
           if(A[X][Y]!=4) face=4;  // 记录每个格子的方向会T 
           if(used[X][Y][a.z][face]) continue;
           if(can(a.face,a,face))
           {
               int xx=a.x+x[a.face],yy=a.y+y[a.face],z=a.z,d=a.d+1;
               if(A[xx][yy]==2) z=1,used[xx][yy][0][face]=1;   // 不加会T 1
               q.push((maple){xx,yy,z,face,d});
               used[xx][yy][z][face]=1;
               continue;
           }
        }
        for(int i=0;i<4;++i)
        {
            int xx=a.x+x[i],yy=a.y+y[i],face=i;
            if(A[xx][yy]!=4) face=4; 
            if(can(i,a,face))
            {
                int z=a.z,d=a.d+1;
                if(A[xx][yy]==4) z=0;
                if(A[xx][yy]==2) z=1,used[xx][yy][0][face]=1;  //不加会T 2
                q.push((maple){xx,yy,z,face,d});
                used[xx][yy][z][face]=1;
            }
        }
    }
}
int main()
{
    freopen("cowboy.in","r",stdin);
    freopen("cowboy.out","w",stdout);
    scanf("%d%d",&N,&M);
    for(int i=1;i<=N;++i)
       for(int j=1;j<=M;++j)
          scanf("%d",&A[i][j]);
    Bfs();
    if(!f) cout<<-1;
    return 0; 
}

//*PS:注释的都是血的教训。。。QAQ

这里写图片描述
这里写图片描述
这里写图片描述


T5 十分明显的exgcd??

再次%%%原创题dalao @loi.MurasameKatana

由于这个题我还是没能调出来,所以我打算十分不要脸的打算粘上题解和std_(:з」∠)_ 。。。。ε=ε=ε=┌(; ・_・)┘跑

而对于本题,首先我们将起点到所有城镇的距离(最短路)排序,每次选取存在妖刀且路程之和为当前最小的两个城镇中的妖刀,取其攻击力为a b,结界初始防御值-临界防御值为c,那么两把妖刀各自的使用次数为方程的解x,y

那么我们首先通过exgcd()求出一组解满足ax + by = gcd(a,b)

注:exgcd()求出的特解为 |x| + |y| 最小的一组解

然后得到满足ax + by = c的解,再推导其他的解找到:

两个使用次数大于0,使用方案不会使任意一把妖刀折断,且消耗的磨损值之和最小的解。

这便是T5胧村正的算法过程

然后就是注意细节处理

by  Loi_MurasameKatana

(侵删)

Std
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define RI register int
using namespace std;
typedef long long ll;
const int INF =1000000007;
ll n,m,h,p,ru,rv,rw,tot,x,y,xs,ys,ans,anz,A,Z,minx,f;
ll dis[100010],first[200010],nxt[200010],c[100010],r[100010],k[100010],t[100010],num[100010];
bool flag;
bool inq[100010],unb[100010];
struct edge
{
    ll u,v,w;
}l[400010];
queue<int>q;
inline bool cmp(ll a,ll b)
{
    if(dis[a]!=dis[b])
    return dis[a]<dis[b];
    else return a<b;
}
inline void build(ll f,ll t,ll c)
{
    l[++tot]=(edge){f,t,c};
    nxt[tot]=first[f];
    first[f]=tot;
}
inline bool check_1(ll a,ll b,ll c)//分情况讨论边界条件 
{
    if(a>0&&b>0)
    {
        if(xs<=0||ys>(k[num[c]]/r[num[c]]))
        return 0;
    }
    else if(a>0&&b<0)
    {
        if(xs>(k[num[c-1]]/r[num[c-1]])||ys>(k[num[c]]/r[num[c]]))
        return 0;
    }
    else if(a<0&&b>0)
    {
        if(xs<=0||ys<=0)
        return 0;
    }
    else if(a<0&&b<0)
    {
        if(xs>(k[num[c]]/r[num[c]])||ys<=0)
        return 0;
    }
    return 1;
}
inline bool check_2(ll a,ll b,ll c)
{
    if(a>0&&b>0)
    {
        if(xs>(k[num[c-1]]/r[num[c-1]])||ys<=0)
        return 0;
    }
    else if(a>0&&b<0)
    {
        if(xs<=0||ys<=0)
        return 0;
    }
    else if(a<0&&b>0)
    {
        if(xs>(k[num[c-1]]/r[num[c-1]])||ys>(k[num[c]]/r[num[c]]))
        return 0;
    }
    else if(a<0&&b<0)
    {
        if(xs<=0||ys>(k[num[c]]/r[num[c]]))
        return 0;
    }
    return 1;
}
inline void SPFA()
{
    for(RI i=1;i<=n;i++)
    dis[i]=INF;
    dis[1]=0;
    q.push(1);
    inq[1]=1;
    while(!q.empty())
    {
        int k=q.front();
        q.pop();
        inq[k]=0;
        for(RI i=first[k];i!=-1;i=nxt[i])
        {
            ll x=l[i].v;
            if(dis[x]>dis[k]+l[i].w)
            {
                dis[x]=dis[k]+l[i].w;
                if(!inq[x])
                {
                    q.push(x);
                    inq[x]=1;
                }
            }
        }
    }
}
inline ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll d=exgcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-a/b*y;
    return d;
}
int main()
{
    freopen("blade.in","r",stdin);
    freopen("blade.out","w",stdout);
    memset(first,-1,sizeof(first));
    scanf("%lld%lld%lld%lld",&n,&m,&h,&p);
    for(RI i=1;i<=m;i++)
    {
        scanf("%lld%lld%lld",&ru,&rv,&rw);
        build(ru,rv,rw);
        build(rv,ru,rw);
    }
    for(RI i=1;i<=n;i++)
    {
        scanf("%lld%lld%lld%lld",&c[i],&r[i],&k[i],&t[i]);
        if(t[i]==1)
        c[i]*=-1;
        if(!c[i]&&!r[i]&&!k[i]&&!t[i])
        unb[i]=1;
    }
    SPFA();
    for(RI i=1;i<=n;i++)
    if(unb[i]==1)
    dis[i]=INF;
    for(RI i=1;i<=n;i++)
    num[i]=i;
    sort(num+1,num+n+1,cmp); // 给城镇排序 
    minx=1e9+7;
    for(RI i=2;i<=n;i+=2)
    {
        if(unb[num[i-1]]||unb[num[i]]) // 若搜遍周边城镇也没有找到 
        break;
        ll d=exgcd(c[num[i-1]],c[num[i]],x,y);//ax+by=gcd(a,b) 
        if(!d||(h-p)%d!=0)  //%0RE 若无解 
        continue;
        ll s=(h-p)/d;//asx+bsy=c 
        x*=s;
        y*=s;
        if(x<y)
        {
            swap(x,y);
            swap(c[num[i-1]],c[num[i]]);
            swap(r[num[i-1]],r[num[i]]);
            swap(k[num[i-1]],k[num[i]]);
        }
        int li=x;
        int lr=y;
        for(RI j=0;;j++)//j>=0 
        {
            xs=x-(c[num[i]]/d)*j;//xs=x-b/d*j
            ys=y+(c[num[i-1]]/d)*j;//ys=y+a/d*j
            if(!check_1(c[num[i-1]]/d,c[num[i]]/d,i))
            break;
            if(xs>0&&ys>0&&xs<=(k[num[i-1]]/r[num[i-1]])&&ys<=(k[num[i]]/r[num[i]]))//合法情况,使用次数大于0且未达次数上限 
            {
                if(((xs*r[num[i-1]]+ys*r[num[i]])<minx)||(A!=0&&Z!=0&&(xs*r[num[i-1]]+ys*r[num[i]]==minx)&&(abs(xs*r[num[i-1]]-ys*r[num[i]])<abs(A*r[num[i-1]]-Z*r[num[i]]))))//取磨损值消耗之和最小的方案 
                {//若存在多解取磨损值相差最小的 
                    minx=(xs*r[num[i-1]]+ys*r[num[i]]);
                    A=xs;
                    Z=ys;
                    flag=1;//存在合法解
                }
            }
        }
        x=li;
        y=lr;
        for(RI j=-1;;j--)//j<0
        {
            xs=x-(c[num[i]]/d)*j;
            ys=y+(c[num[i-1]]/d)*j;//x为大正数,y为大负数的情况不会导致非法退出
            if(!check_2(c[num[i-1]]/d,c[num[i]]/d,i))
            break;
            if(xs>0&&ys>0&&xs<=(k[num[i-1]]/r[num[i-1]])&&ys<=(k[num[i]]/r[num[i]]))
            {
                if(((xs*r[num[i-1]]+ys*r[num[i]])<minx)||(A!=0&&Z!=0&&(xs*r[num[i-1]]+ys*r[num[i]]==minx)&&(abs(xs*r[num[i-1]]-ys*r[num[i]])<abs(A*r[num[i-1]]-Z*r[num[i]]))))
                {
                    minx=(xs*r[num[i-1]]+ys*r[num[i]]);
                    A=xs;
                    Z=ys;
                    flag=1;
                }
            }
        }
        if(flag)
        {
            ans=min(A*r[num[i-1]],Z*r[num[i]]);//统计 
            anz=max(A*r[num[i-1]],Z*r[num[i]]);
            break;
        }
    }
    if(flag)
    {
        printf("%lld %lld\n",ans,anz);
    }
    else printf("Game over\n");
    return 0;
}

The end.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值