*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.