题目:https://cn.vjudge.net/contest/176068#overview
Uva 12260
做法:Petra的策略满足贪心,所以先把糖果按P在按J排序,然后去取,就看Jan会取哪些糖果了。
每次默认Jan先取,如果petra先取的话,i从2开始循环。
那么每个糖果就可以看成是Jan取不取,但是要注意,由于Petra是一定会往后一个取,所以Jan取糖果的时候是有一定的限制的,该限制为:假如都是Jan先取,1个糖果,Jan可以拿到,2个糖果,Jan可以拿其中一个,3个糖果Jan可以拿其中两个,4个糖果也是其中两个,以此类推,Jan能拿的糖果数为(i + 1)/2。因此dp[i][j]表示Jan在前i个糖果中拿了j个,j <= (i + 1)/2
#include <bits/stdc++.h>
using namespace std;
struct node
{
int va,vb;
}val[1100];
bool cmp(const struct node a,const struct node b)
{
if(a.va!=b.va) return a.va>b.va;
return a.vb < b.vb;
}
char s[30];
int dp[1100][1100];
int cost[1100][1100];
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
scanf("%s",s);
int sum=0;
for(int i=1;i<=n;++i)
{
scanf("%d%d",&val[i].va,&val[i].vb);
sum+=val[i].va;
}
sort(val+1,val+1+n,cmp);
memset(dp,0,sizeof(dp));
memset(cost,0,sizeof(cost));
int cnt=0;
for(int i=(s[0]=='P')? 2:1 ;i<=n;++i)
{
++cnt;
for(int j=1;j<=(cnt+1)/2;++j)
{
dp[i][j] = dp[i-1][j];
cost[i][j] = cost[i-1][j];
if(dp[i-1][j-1] + val[i].vb > dp[i][j])
{
dp[i][j] = dp[i-1][j-1] + val[i].vb;
cost[i][j] = cost[i-1][j-1] + val[i].va;
}
else if(dp[i-1][j-1] + val[i].vb == dp[i][j])
{
cost[i][j] = min(cost[i][j],cost[i-1][j-1]+val[i].va);
}
}
}
printf("%d %d\n",sum-cost[n][(cnt+1)/2],dp[n][(cnt+1)/2]);
}
return 0;
}
HDU 4662
做法: 把U都变成I 统计有多少个I 。 如果满足 2^K - num = 6 * T ; 那么就是Yes,只需要枚举比num大 两次即可
Uva 1362
做法:dp[i][j]表示从i到j的位置可以用多少种多叉树表示。转移方程:dp[i][j]=∑(k=i+2->j)dp[i+1][k−1]∗dp[k][j]。
相当于每次枚举一个子树的位置即可,因为要回溯,所以要枚举s[i]=s[k]
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll mod = 1000000000;
ll dp[320][320];
char s[310];
ll cal(int l,int r)
{
if(dp[l][r]!=-1) return dp[l][r];
if(l==r) return 1;
if(s[l] != s[r]) return 0;
dp[l][r]=0;
for(int i=l+2;i<=r;++i)
{
if(s[l]==s[i] && s[r]==s[i])
dp[l][r] = (dp[l][r] + (ll)cal(l+1,i-1)*(ll)cal(i,r))%mod;
}
return dp[l][r]%mod;
}
int main()
{
freopen("exploring.in","r",stdin);
freopen("exploring.out","w",stdout);
while(scanf("%s",s)!=EOF)
{
memset(dp,-1,sizeof(dp));
printf("%I64d\n",cal(0,strlen(s)-1));
}
return 0;
}
CF 678C
做法: 设dp[i][j] , 意为 i->j 是否可取。 然后做01背包。 不难发现 如果 dp[i][j]可取 ,那么dp[i+a[k]][j] dp[i+a[k]][j+a[k]]
也都可以取。
for(int i=1;i<=n;++i)
{
for(int k=aim;k>=a[i];--k)
{
for(int j=0;j+a[i]<=aim;++j)
{
if(dp[k-a[i]][j])
{
dp[k][j]=dp[k][j+a[i]]=1;
}
}
}
}
HDU 4960
做法:首先要预处理出所有关键点(i,j) 满足 sum[1->n] == sum[j->n]。然后 做状态dp[]的一维状态转移即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[510000];
int n;
int a[5100];
int v[5100];
int qsum[5100];
int hsum[5100];
struct node
{
int a,b;
}book[510000];
bool cmp(const node x,const node y)
{
if(x.a != y.a) return x.a < y.a;
return x.b>y.b;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
if(n==0) break;
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i) scanf("%d",&v[i]);
qsum[0]=0;
for(int i=1;i<=n;++i) qsum[i] = qsum[i-1] + a[i];
hsum[n+1]=0;
for(int i=n;i>=1;--i) hsum[i] = hsum[i+1] +a[i];
int cnt=1;
book[0].a=0; book[0].b=n+1;
for(int i=1;i<=n;++i)
{
for(int j=i+1;j<=n;++j)
{
if(qsum[i]==hsum[j])
{
book[cnt].a = i;
book[cnt++].b = j;
}
}
}
sort(book,book+cnt,cmp);
ll ans=v[n];
for(int i=0;i<cnt;++i) dp[i]=0x3f3f3f3f3f3f;
dp[0]=0;
for(int i=1;i<cnt;++i)
{
for(int j=i-1;j>=0;j--)
{
int l=book[i].a-book[j].a;
int r=book[j].b-book[i].b;
dp[i] = min(dp[i],dp[j]+v[l]+v[r]);
}
ans= min( ans, dp[i] + v[ book[i].b-book[i].a-1]);
}
printf("%I64d\n",ans);
}
return 0;
}
BZOJ 4806
做法:dp[i][j] 意为在m列中还有多少列有0个炮,有1个炮 的方案数。每次按行转移。有一种不放的情况,两种放一个炮的情况,三种放两个炮的情况。慢慢转移就行了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=999983;
int n,m;
ll dp[110][110][110];
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(dp,0,sizeof(dp));
dp[0][m][0]=1;
int ans=0;
for(int t=1;t<=n;++t)
{
for(int i=0;i<=m;++i)
{
for(int j=0;j<=m;++j)
{
dp[t][i][j]=dp[t-1][i][j]%mod;
dp[t][i][j]=(dp[t][i][j] +((j+1)*dp[t-1][i][j+1])%mod)%mod;
if(j>=1) dp[t][i][j]=(dp[t][i][j] +((i+1)*dp[t-1][i+1][j-1])%mod)%mod;
dp[t][i][j]=(dp[t][i][j] +((j+2)*(j+1)*dp[t-1][i][j+2]/2) %mod )%mod;
dp[t][i][j]=(dp[t][i][j] +((i+1)*(j)*dp[t-1][i+1][j]) %mod)%mod;
if(j>=2) dp[t][i][j]=(dp[t][i][j] +((i+2)*(i+1)*dp[t-1][i+2][j-2]/2) %mod )%mod;
if(t==n) ans=(ans+dp[t][i][j])%mod;
}
}
}
printf("%lld\n",ans%mod);
}
return 0;
}
HDU 5543
做法:比普通的01背包多加了一维状态,意为当前两边放了几个。转移的时候 三种形式放置,一种放在边上,一种放在普通位置,一种不放
注意:这里把 L 和每个棒的长度都乘以2 ,因为 左右两边只需要放一半的长度就可以放下(避免奇数)
同时这道题有个BUG ,当只有一个木棍的时候,怎么都能放下。(要特判),01背包滚动数组时候,要逆向遍历当前背包的大小
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,l;
ll dp[2005*2][4];
int len[1005];
ll val[1005];
int main()
{
int T;
int kiss=0;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&l);
l=l*2;
for(int i=1;i<=n;++i)
{
scanf("%d %I64d",&len[i],&val[i]);
len[i]= len[i]*2;
}
if(n==1)
{
printf("Case #%d: %I64d\n",++kiss,val[1]);
continue;
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;++i)
{
for(int j=l;j>=(len[i]/2);--j)
{
for(int k=0;k<=2;++k)
{
if(k<=1) dp[j][k] = max(dp[j][k],dp[j-(len[i]/2)][k+1]+val[i]);
if(j>=len[i]) dp[j][k] = max(dp[j][k],dp[j-len[i]][k]+val[i]);
}
}
}
ll ans=0;
for(int j=0;j<=l;++j)
{
for(int k=0;k<=2;++k)
{
ans=max(dp[j][k],ans);
}
}
printf("Case #%d: %I64d\n",++kiss,ans);
}
return 0;
}