比赛地址: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;
}