2017 清北济南考前刷题Day 6 morning
T1
贪心
10 元先找5元
20元 先找10+5,再找3张5
#include<cstdio> using namespace std; int m5,m10,m20; int main() { freopen("book.in","r",stdin); freopen("book.out","w",stdout); int n; scanf("%d",&n); int x; for(int i=1;i<=n;i++) { scanf("%d",&x); if(x==5) m5++; else if(x==10) { if(!m5) { puts("NO"); return 0; } m5--; m10++; } else { if(m10 && m5) m10--,m5--; else if(m5>=3) m5-=3; else { puts("NO"); return 0; } } } puts("YES"); return 0; }
T2
小c记得的出口位置看做左括号
从右往左扫描 信息
左括号入栈
否则判断当前栈顶 是否能匹配,
匹配则为右括号,不匹配当左括号入栈
栈空则有解,非空则无解
#include<cstdio> #include<iostream> #include<algorithm> #define N 1000001 using namespace std; int a[N]; int st[N],top; bool zf[N]; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } int main() { freopen("program.in","r",stdin); freopen("program.out","w",stdout); int n,m; read(n); for(int i=1;i<=n;++i) read(a[i]); read(m); int x; for(int i=1;i<=m;++i) { read(x); if(a[x]>0) a[x]=-a[x]; } for(int i=n;i;--i) { if(a[i]<0) st[++top]=a[i],zf[i]=true; else { if(st[top]==-a[i]) top--; else st[++top]=-a[i],zf[i]=true; } } if(top) { puts("NO"); return 0; } for(int i=1;i<=n;i++) { if(!zf[i]) printf("+%d ",abs(a[i])); else printf("-%d ",abs(a[i])); } fclose(stdin); fclose(stdout); return 0; }
不会spj所以不知道对错的代码
对于一个出口,判断前面是否有足够的入口
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define N 1000001 int a[N],b[N]; int tot[N],pre[N]; int outtot[N]; bool out[N],zf[N]; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } } int main() { freopen("program.in","r",stdin); freopen("program.out","w",stdout); int n; read(n); int x; for(int i=1;i<=n;i++) { read(x); a[i]=x; pre[i]=++tot[x]; } int m; read(m); for(int i=1;i<=m;++i) read(b[i]); sort(b+1,b+m+1); int t=unique(b+1,b+m+1)-b-1; for(int i=1;i<=t;i++) { x=b[i]; outtot[a[x]]++; out[x]=true; if(pre[x]<outtot[a[x]]*2) { puts("NO"); return 0; } } for(int i=1;i<=n;i++) if(tot[i]&1) { puts("NO"); return 0; } for(int i=1;i<=n;i++) { if(out[i]) printf("-%d ",a[i]); else if(outtot[a[i]]) printf("+%d ",a[i]),outtot[a[i]]--; else if(!zf[a[i]]) printf("+%d ",a[i]),zf[a[i]]^=1; else printf("-%d ",a[i]),zf[a[i]]^=1; } }
T3
把钥匙看做左括号,门看做右括号
模拟括号匹配,任意两点间只有两种状态:匹配,缺右括号
那么问题转化为 从a走到b 能否括号匹配
宽搜+DP
dp[i][j][k]表示 点i到点j 是否有状态k
k=0 表示 i到j的路径上能够括号匹配
k:1——10 表示 点i到j的路径上,栈顶为右括号k
k:11——20 表示点i到j的路径上,栈顶为左括号k,即缺右括号k
每次更新一个ijk,就相当于在ij之间连一条状态为k的边,扔进队列里
从队列里取出 从u到v的状态为w的边
如果w=0,那么 枚举状态k (0,11——20)
if dp[i][u][k]=true 更新 dp[i][v][k]
if dp[v][i][k]=true 更新 dp[u][i][k]
如果w!=0,那么 只能去找右括号
即if dp[v][i][w-10] 更新 dp[u][i][0]
为什么 w=0 是双向更新,w!=0是单项更新?
因为只能是栈中有左括号的情况下,右括号才能入栈
对于每一次询问,判断dp[u][v][0] 即可
#include<cstdio> using namespace std; bool dp[101][101][21]; int head,tail; int q[100001][3]; int main() { freopen("maze.in","r",stdin); freopen("maze.out","w",stdout); int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) dp[i][i][0]=true; int u,v,w; while(m--) { scanf("%d%d%d",&u,&v,&w); if(!w) { dp[u][v][w]=dp[v][u][w]=true; q[tail][0]=u; q[tail][1]=v; q[tail++][2]=w; q[tail][0]=v; q[tail][1]=u; q[tail++][2]=w; } else if(w<0) { w=-w; dp[u][v][w]=dp[v][u][w]=true; } else { w+=10; dp[u][v][w]=dp[v][u][w]=true; q[tail][0]=u; q[tail][1]=v; q[tail++][2]=w; q[tail][0]=v; q[tail][1]=u; q[tail++][2]=w; } } while(head<tail) { u=q[head][0]; v=q[head][1]; w=q[head++][2]; if(!w) { for(int i=1;i<=n;++i) { if(dp[i][u][0] && !dp[i][v][0]) { dp[i][v][0]=true; q[tail][0]=i; q[tail][1]=v; q[tail++][2]=0; } if(dp[v][i][0] && !dp[u][i][0]) { dp[u][i][0]=true; q[tail][0]=u; q[tail][1]=i; q[tail++][2]=0; } for(int j=11;j<=20;++j) { if(dp[i][u][j] && !dp[i][v][j]) { dp[i][v][j]=true; q[tail][0]=i; q[tail][1]=v; q[tail++][2]=j; } } } } else { for(int i=1;i<=n;++i) { if(dp[v][i][w-10] && !dp[u][i][0]) { dp[u][i][0]=true; q[tail][0]=u; q[tail][1]=i; q[tail++][2]=0; } } } } int q; scanf("%d",&q); while(q--) { scanf("%d%d",&u,&v); puts(dp[u][v][0] ? "YES" : "NO"); } }