USACO 2.3.1
题解:
DP,DP[i]表示长度为i的前缀是否能够分解。
代码:
/* ID:m1599491 PROG:prefix LANG:C++ */ #include<ciostream> #include<cstdio> #include<cstring> #define INF 10000000 #define MAX_N 205 #define MAX_L 200005 using namespace std; int n=0; int ans=0; bool dp[MAX_L]; string s; string p[MAX_N]; void read() { while(cin>>p[n]) { if(p[n]==".") break; n++; } string temp; while(cin>>temp) { s+=temp; } } void solve() { memset(dp,false,sizeof(dp)); dp[0]=true; for(int i=0,t=s.size();i<t;i++) { if(dp[i]) { for(int j=0;j<n;j++) { bool flag=true; for(int k=0;k<p[j].size();k++) { if(s[k+i]!=p[j][k]) { flag=false; break; } } if(flag) dp[i+p[j].size()]=true; } } } for(int i=0;i<=s.size();i++) if(dp[i]) ans=i; } void print() { cout<<ans<<endl; } int main() { freopen("prefix.in","r",stdin); freopen("prefix.out","w",stdout); read(); solve(); print(); }
USACO 2.3.2
题解:
DP,f[i][j]表示i个点构成深度为j的二叉树的方案数。
不难得出,构成满足条件的二叉树的方案数=左子树的方案数*右子树的方案数。
所以,要满足二叉树的深度为j,左子树和右子树两个之间必定至少有一棵树的深度为j-1。
因此可以分三种情况讨论:1、左子树深度=j-1,右子树深度≤j-2;2、左子树深度≤j-2,右子树深度=j-1;3、左右子树深度均为j-1。
因此要新建一个数组t[i][j]表示所有由i个点组成,深度≤j的树的个数。
代码:
/* ID:m1599491 LANG:C++ TASK:nocows */ #include<cstdio> #include<cmath> #include<iostream> #include<cstring> #define Mod 9901 #define MaxN 200 #define MaxK 100 using namespace std; int n,k,i,j,kk; int f[MaxN][MaxK]={0},t[MaxN][MaxK]={0}; main() { freopen("nocows.in", "r", stdin); freopen("nocows.out", "w", stdout); scanf("%d%d",&n,&k); f[1][1]=1; for (i=1; i<=n; i+=2) { for (j=2; j<=k; j++) { for (kk=1; kk<=i-2; kk+=2) { f[i][j]=(f[i][j]+f[kk][j-1]*f[i-1-kk][j-1])%Mod; f[i][j]=(f[i][j]+t[kk][j-2]*f[i-1-kk][j-1])%Mod; f[i][j]=(f[i][j]+f[kk][j-1]*t[i-1-kk][j-2])%Mod; } t[i][j-1]=(f[i][j-1]+t[i][j-2])%Mod; } } printf("%d ",f[n][k]); }
USACO 2.3.3
题解:
先算一波N个由0..2构成的元素的全排列,构成一个就check一下,符合就输出。(在全排列那里改一下顺序,输出前就不用排序了233
代码:
/* ID:m1599491 PROG:zerosum LANG:C++ */ #include<cstdio> #include<iostream> #include<cmath> using namespace std; int n,i; int a[10],c[10]; void check() { int sum=a[1],x=0,cc=1; for (i=1; i<n; i++) { if (c[i]==2) sum=sum*10+a[i+1]; if (c[i]==1 || c[i]==0) { if (cc==1) x+=sum; else if (cc==0) x-=sum; cc=c[i]; sum=a[i+1]; } } if (cc==0) x-=sum; else if (cc==1) x+=sum; if (x!=0) return; for (i=1; i<=n; i++) { if (i==n) {printf("%d ",a[i]);break;} if (c[i]==1) printf("%d+",a[i]); if (c[i]==0) printf("%d-",a[i]); if (c[i]==2) printf("%d ",a[i]); } } void dfs(int dep) { int i,j; if (dep==n) {check();return;} c[dep]=2;dfs(dep+1); c[dep]=1;dfs(dep+1); c[dep]=0;dfs(dep+1); } main() { freopen("zerosum.in","r",stdin); freopen("zerosum.out","w",stdout); scanf("%d",&n); for (i=1; i<=n; i++) a[i]=i; dfs(1); return 0; }
USACO 2.3.4
题解:
背包改一改嘛(躺
f[j]表示构成j块钱的方案数,输出f[n]。
代码:
/* ID:m1599491 PROG:money LANG:C++ */ #include<cmath> #include<iostream> #include<cstdio> #include<cstring> #define MaxN 10005 #define MaxV 30 #define ll long long using namespace std; ll v,n,m,f[MaxN]; int i,j; main() { freopen("money.in","r",stdin); freopen("money.out","w",stdout); scanf("%lld%lld",&v,&n); f[0]=1; for (i=1; i<=v; i++) { scanf("%lld",&m); for (j=m; j<=n; j++) f[j]+=f[j-m]; } printf("%lld ",f[n]); }
USACO 2.3.5
题解:
DFS嘛,c[i]表示i公司被当前枚举到的公司控制的股份,如果c[i]>50就继续DFS下去,最后枚举一波,大于50的就直接输出了,然后就memset。(由于我是个傻逼,没有加头文件cstring,看着memset报错懵逼了好久
代码:
/* ID:m1599491 PROG:concom LANG:C++ */ #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; int f[110][110],c[110]; bool use[110]={0}; int n,i,x,y,z,m,j; void dfs(int x) { use[x]=1; int i; for (i=1; i<=n; i++) c[i]+=f[x][i]; for (i=1; i<=n; i++) if (c[i]>50 && !use[i]) dfs(i); } main() { freopen("concom.in","r",stdin); freopen("concom.out","w",stdout); scanf("%d",&m); for (i=1; i<=m; i++) { scanf("%d%d%d",&x,&y,&z); f[x][y]=z; n=max(n,max(x,y)); } for (i=1; i<=n; i++) { memset(c,0,sizeof(c)); memset(use,false,sizeof(use)); dfs(i); for (j=1; j<=n; j++) if (i!=j && c[j]>50) printf("%d %d ",i,j); } }