T1水题:
P1328 生活大爆炸版石头剪刀布
#include<bits/stdc++.h> using namespace std; #define N 205 #define ri register int int a[N],b[N]; int check(int a,int b) { if(a==b) return 0; if(a==0){ if(b==1 ||b==4) return 2; return 1; } if(a==1){ if(b==2 || b==4) return 2; return 1; } if(a==2){ if(b==0 || b==3) return 2; return 1; } if(a==3){ if(b==0 || b==1) return 2; return 1; } if(a==4){ if(b==3 || b==2) return 2; return 1; } return 0; } int main() { int n,n1,n2; scanf("%d%d%d",&n,&n1,&n2); for(ri i=1;i<=n1;++i) scanf("%d",&a[i]); for(ri i=1;i<=n2;++i) scanf("%d",&b[i]); int i=1,j=1,A=0,B=0; while(n--){ if(i>n1) i=1; if(j>n2) j=1; int x=check(a[i],b[j]); //printf("a:%d b:%d x:%d\n",a[i],b[j],x); if(x==1) A++; else if(x==2) B++; i++,j++; } printf("%d %d\n",A,B); } /* 10 5 6 0 1 2 3 4 0 3 4 2 1 0 */
T2水题:
P1351 联合权值
#include<bits/stdc++.h> using namespace std; #define ll long long #define N 200005 #define mod 10007 #define ri register int ll ans=0,maxx=0,lmx[N],mx[N],sum[N],w[N]; int tot=0,head[N],nex[N<<1],to[N<<1],fa[N]; void add(int a,int b) { to[++tot]=b; nex[tot]=head[a]; head[a]=tot; } void dfs1(int u,int ff) { for(ri i=head[u];i;i=nex[i]){ int v=to[i]; if(v==ff) continue; sum[u]+=w[v]; //sum[u]%=mod; 注意sum不要取mod 否则会在下面减成负数!!! if(!mx[u]) mx[u]=w[v]; else if(w[v]>mx[u]) lmx[u]=mx[u],mx[u]=w[v]; else if(w[v]>lmx[u]) lmx[u]=w[v]; fa[v]=u; dfs1(v,u); } } void dfs2(int u,int ff) { for(ri i=head[u];i;i=nex[i]){ int v=to[i]; if(v==ff) continue; ans+=w[v]*(sum[u]-w[v]) %mod; ans%=mod; ans+=w[v]*w[ff] %mod *2 %mod; ans%=mod; maxx=max(maxx,w[v]*w[ff]); dfs2(v,u); } maxx=max(maxx,mx[u]*lmx[u]); } int main() { int n,a,b; scanf("%d",&n); for(ri i=1;i<=n-1;++i) scanf("%d%d",&a,&b),add(a,b),add(b,a); for(ri i=1;i<=n;++i) scanf("%lld",&w[i]); dfs1(1,0); dfs2(1,0); printf("%lld %lld\n",maxx,ans%mod); } /* 6 1 2 1 3 1 4 2 6 2 5 1 2 3 4 5 6 */
T3简单dp:
P1941 飞扬的小鸟
分析:
对于简单的模拟水题,应该看清楚题目要求和数据范围。
对于有减法又有%的题,注意不要减成负数,要( ……+mod )%mod。
T3的直接dp,复杂度大,只能得75分:
#include<bits/stdc++.h> using namespace std; #define inf 0x7f7f7f #define N 10005 #define M 1005 #define ll long long #define ri register int int x[N],y[N]; ll dp[N][M]; struct node{ int u,v,fl; }g[N]; int main() { int n,m,k,a,b,c,fll=0,tmp; scanf("%d%d%d",&n,&m,&k); for(ri i=0;i<=n-1;++i) scanf("%d%d",&x[i],&y[i]); for(ri i=1;i<=k;++i) scanf("%d%d%d",&a,&b,&c),g[a].u=b,g[a].v=c,g[a].fl=1; for(ri i=0;i<=n;++i) if(!g[i].fl) g[i].u=0,g[i].v=m+1; for(ri i=0;i<=n;++i) for(ri j=0;j<=m;++j) dp[i][j]=inf; for(int i=g[0].u;i<=g[0].v;++i) dp[0][i]=0; for(ri i=0;i<=n-1;++i){// bool fl=0; for(ri j=g[i].u+1; j<=g[i].v-1 ; ++j){ int pos= j-y[i] ;//上一次不点 这一次的高度 if( pos<g[i+1].v && pos>g[i+1].u ) //可以通过这一个管道 dp[i+1][pos]=min(dp[i+1][pos] , dp[i][j]); int up=( g[i+1].v-j )/x[i] +1, down= ( g[i+1].u-j )/x[i];//上一次最多点的次数和最少点的次数 down=max(1,down); for(ri k=down;k<=up;++k){ pos=j+x[i]*k;//点k次后这一次的高度 if( pos>0 && (!g[i+1].fl) && pos>m ) pos=m; if( pos<g[i+1].v && pos>g[i+1].u ) dp[i+1][pos]=min(dp[i+1][pos] , dp[i][j]+k); } } for(ri j=g[i+1].u+1; j<=g[i+1].v-1; ++j) if(dp[i+1][j]!=inf) fl=1; if(!fl) { fll=1; tmp=i; break; } } if(fll){//统计最多到的水管数 ll ans=0; for(ri i=1;i<=tmp;++i) ans+=g[i].fl; printf("0\n%lld\n",ans); return 0; } ll ans=inf; for(ri i=g[n].u+1;i<=g[n].v-1;++i) ans=min(ans,dp[n][i]); printf("1\n%lld\n",ans); return 0; } /* 10 10 6 3 9 9 9 1 2 1 3 1 2 1 1 2 1 2 1 1 6 2 2 1 2 7 5 1 5 6 3 5 7 5 8 8 7 9 9 1 3 */
正解是背包或者利用背包的思路进行优化:
#include<bits/stdc++.h> using namespace std; #define inf 0x7f7f7f #define N 10005 #define M 2005 #define ll long long #define ri register int int x[N],y[N]; ll dp[N][M]; struct node{ int u,v,fl; }g[N]; int main() { int n,m,k,a,b,c,fll=0,tmp; scanf("%d%d%d",&n,&m,&k); for(ri i=1;i<=n;++i) scanf("%d%d",&x[i],&y[i]); for(ri i=1;i<=k;++i) scanf("%d%d%d",&a,&b,&c),g[a].u=b,g[a].v=c,g[a].fl=1; for(ri i=0;i<=n;++i) if(!g[i].fl) g[i].u=0,g[i].v=m+1; for(ri i=0;i<=n;++i) for(ri j=0;j<=m+1;++j) dp[i][j]=inf; for(int i=g[0].u+1;i<=g[0].v-1;++i) dp[0][i]=0; for(ri i=1;i<=n;++i){ for(ri j=x[i]+1;j<=m+x[i];++j)//上升是完全背包 dp[i][j]=min( dp[i-1][j-x[i]]+1 , dp[i][j-x[i]]+1 ); for(ri j=m+1;j<=m+x[i];++j)//处理超过m的部分 dp[i][m]=min(dp[i][m],dp[i][j]); for(ri j=1;j<=m-y[i];++j)//下降是01背包 dp[i][j]=min(dp[i][j],dp[i-1][j+y[i]]); for(ri j=0;j<=g[i].u;++j) dp[i][j]=inf;//将不能到达的地方赋成inf for(ri j=g[i].v;j<=m;++j) dp[i][j]=inf; } ll ans=inf; int fl=0; for(ri i=1;i<=m;++i) ans=min(ans,dp[n][i]); if(ans==inf){ int pos; for(ri i=n;i>=0;--i){//倒着回去看 是哪个横坐标飞不过去了 for(ri j=1;j<=m;++j) if(dp[i][j]<inf) { fl=1; break ; } if(fl) { pos=i; break; } } ans=0; for(ri i=0;i<=pos;++i) ans+=g[i].fl;//统计水管数 printf("0\n%lld\n",ans); return 0; } printf("1\n%lld\n",ans); return 0; } /* 10 10 6 3 9 9 9 1 2 1 3 1 2 1 1 2 1 2 1 1 6 2 2 1 2 7 5 1 5 6 3 5 7 5 8 8 7 9 9 1 3 */
总之,题虽简单,还是有很多细节要注意的。