比赛地址:http://vjudge.net/contest/view.action?cid=46147#overview
A - Longest Ordered Subsequence(POJ2533)
解题思路:最长上升子序列,状态转移公式:dp[i]=max(dp[i],dp[j]+1) (num[j]<num[i])
但是这题还是很让人蛋碎的,就是如果用while(scanf)会wa,当然,我是这种情况的
#include<iostream> #include<cstdio> #include<cstring> #include<cctype> using namespace std; #define MAX 1111 int n,num[MAX],dp[MAX]; void LIS() { for (int i = 1; i <= n; ++i) dp[i] = 1; for (int i = 1; i <= n; ++i) for (int j = 1; j < i; ++j) if (num[j] < num[i]) dp[i] = max(dp[i], dp[j] + 1); } int main() { int maxn=0; scanf("%d", &n); for (int i = 1; i <= n; ++i) scanf("%d", &num[i]); LIS(); for (int i = 1; i <= n; ++i) maxn = max(maxn, dp[i]); printf("%d\n", maxn); return 0; }
B题(POJ3624):
解题思路:裸01背包
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int dp[13000]; int n,m; int w[3500],d[3500]; int main() { scanf("%d %d",&n,&m); for(int i=0;i<n;i++) scanf("%d %d",&w[i],&d[i]); memset(dp,0,sizeof(dp)); for(int i=0;i<n;i++) for(int j=m;j>=w[i];j--) dp[j]=max(dp[j],dp[j-w[i]]+d[i]); printf("%d\n",dp[m]); return 0; }
C题(POJ2063):
解题思路:完全背包,由于tot和a都是 1000的倍数,所有在计算时可以把他们缩小1000倍,这样节约内存和时间。
因为years<=40,按照暴力的思想,将每一年都用完全背包求出这一年最大可以得到的利息,然后下一年再用加上利息后的总钱继续计算下去
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cctype> using namespace std; struct Node { int w,v; }a[15]; int n,y,m; int dp[1000010]; bool cmp(Node a,Node b) { return a.w<b.w; } int main() { int T; scanf("%d",&T); while(T--) { memset(dp,0,sizeof(dp)); scanf("%d %d",&m,&y); scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d %d",&a[i].w,&a[i].v); a[i].w/=1000; } // sort(a,a+n,cmp); while(y--) { int temp=m/1000; for(int i=0;i<n;i++) for(int j=a[i].w;j<=temp;j++) dp[j]=max(dp[j],dp[j-a[i].w]+a[i].v); m+=dp[temp]; } printf("%d\n",m); } return 0; }
D题(CodeForces 245H):线段DP
解题思路:
先用DP或者记忆话搜索求出is[i][j](表示i,j之间的字符串是否为回文),这一点是最关键的,用暴力检测的话会超时。
状态转移方程dp[l][r]=dp[l+1][r]+dp[l][r-1]-dp[l+1][r-1]+is[l][r]
#include<cstdio> #include<cstring> #include<string> #include<iostream> #include<algorithm> #include<cctype> using namespace std; int dp[5050][5050],q,is[5050][5050]; char a[5050]; int main() { // freopen("data.txt","r",stdin); scanf("%s",a); int len=strlen(a); for(int i=0;i<len;i++) dp[i][i]=is[i][i]=1; for(int i=2;i<=len;i++) { for(int l=0;l<len+1-i;l++) { int r=l+i-1; if((l+1>r-1 || is[l+1][r-1]) && a[l]==a[r]) is[l][r]=1; dp[l][r]=dp[l+1][r]+dp[l][r-1]-dp[l+1][r-1]+is[l][r]; } } scanf("%d",&q); while(q--) { int temp1,temp2; scanf("%d %d",&temp1,&temp2); printf("%d\n",dp[temp1-1][temp2-1]); } return 0; }
E题(CodeForces 161D)
解题思路:
树状DP,这个公式太复杂了,又臭又长,而且据焦级长所说,就是你听了半天还是不会理解就是了
大家百度一下吧。
#include<cstdio> #include<cstring> #include<string> #include<iostream> #include<algorithm> #include<cctype> using namespace std; #define MAX 50010 struct Node { int v; int next; } E[2*MAX]; int head[MAX];//其实以后推荐用vector写动态的领接表 int dp[MAX][510]; bool vis[MAX]; int num,n,k,ans; void init() { memset(head,-1,sizeof(head)); memset(dp,0,sizeof(dp)); memset(vis,0,sizeof(vis)); num=0; ans=0; } void add(int s,int t) { E[num].v=t; E[num].next=head[s]; head[s]=num++; } void dfs(int cur) { vis[cur]=1; dp[cur][0]=1; int i,j; for(i=head[cur];i!=-1;i=E[i].next) { int v=E[i].v; if(!vis[v]) { dfs(v); for(j=0;j<k;j++)//另一棵子树 ans+=dp[cur][j]*dp[v][k-j-1]; for(j=1;j<=k;j++)//回到父节点 dp[cur][j]+=dp[v][j-1]; } } } int main() { // freopen("data.txt","r",stdin); while(scanf("%d %d",&n,&k) != EOF) { init(); int s,t; for(int i=0;i<n-1;i++) { scanf("%d %d",&s,&t); add(s,t); add(t,s); } dfs(1); printf("%d\n",ans); } }
F题(CodeForces 414B):
解题思路:
筛法,具体看代码就能够理解了吧
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cctype> using namespace std; const int MOD=1000000007; int dp[2002][2002];//len,num of the end int main() { int n,k; scanf("%d %d",&n,&k); for (int i=1;i<=n; i++) dp[1][i] = 1; for (int i=1;i<=k;i++) for (int j=1;j<=n;j++) for (int z=j;z<=n;z+=j) dp[i][z]=(dp[i][z]+dp[i-1][j])%MOD; int ans = 0; for (int i=1;i<=n;i++) { ans+=dp[k][i]; ans%=MOD; } printf("%d\n",ans); return 0; }