前言
这次考试整体都不是很好,我也一样的挂的非常非常的惨,这中类dp题竟然这么难吗?
试题
1.巨魔有金币(gold.pas/c/cpp)
Time Limit:0.1s Memory Limit:256MB
【题目描述】
某巨魔去了一趟拍卖行卖东西,赚了不少金币。
该巨魔由于报复社会不成被社会报复后决定报复做题的众人。已知现在在 WOW 中有 4种面值的金币(我骗你们的,我说了我要报复你们!哦呵呵!),面值大小分别为 c1、c2、c3、c4。巨魔问你 T 次,每次询问:当他有 si 枚面值为 ci 的金币时,他有多少种付钱方式买下价值为 V 的物品,两种付钱方式不同当且仅当两种付钱方式中存在至少一种面值的金币使用的数量不同。
【输入格式】
第一行 5 个数字 c1、c2、c3、c4、T。
接下来 T 行,每行 5 个数字,分别为 s1、s2、s3、s4、V。
【输出格式】
输出 T 行,每行一个数字,各对应一个询问的答案。
【样例输入输出】
Gold.in
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900Gold.out
4
27
【数据范围】
100%的数据 1≤T≤1000 、 1≤si,V≤100000 。
【题解】
做过原题的我考试结果打挂了,结果是for循环搞错了方向…
这道题目就是不可以重复使用的01背包问题。用dp预处理完之后再用容斥原理减值或者加值。
设
dp[i]
为“不限制硬币”下的最多方法数,那么状态转移方程就是
dp[j]=dp[j]+dp[j−c[i]]
。
解决完这些后,我们来看容斥原理
设第1枚硬币超过最大值,也就是大于携带的数量。那么就要将总方案数减去第一枚硬币超过最大值的方案,以此类推。那么按照容斥定理,单数的我们就要减去,双数我们就要加上。这样就能得出正确答案。
大概就这样就可以了。
【代码】
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int size = 110000;
LL f[size];
LL c[size],d[size];
int main() {
freopen("gold.in","r",stdin);
freopen("gold.out","w",stdout);
LL T,tot;
for(int i=1;i<=4;i++)scanf("%lld",&c[i]);
scanf("%lld",&T);
memset(f,0,sizeof(f));f[0]=1;
for(int i=1;i<=4;i++)
for(int j=c[i];j<=100000;j++)
f[j]+=f[j-c[i]];
while(T--) {
for(int i=1;i<=4;i++) {scanf("%d",&d[i]);d[i]++;}
scanf("%lld",&tot);
LL ans=f[tot];
for(int i=1;i<=4;i++)if(tot>=d[i]*c[i])ans-=f[tot-d[i]*c[i]];
for(int i=1;i<=3;i++)for(int j=i+1;j<=4;j++)if(tot>=d[i]*c[i]+d[j]*c[j])ans+=f[tot-d[i]*c[i]-d[j]*c[j]];
for(int i=1;i<=2;i++)for(int j=i+1;j<=3;j++)for(int k=j+1;k<=4;k++)if(tot>=d[i]*c[i]+d[j]*c[j]+d[k]*c[k])ans-=f[tot-d[i]*c[i]-d[j]*c[j]-d[k]*c[k]];
if(tot>=d[1]*c[1]+d[2]*c[2]+d[3]*c[3]+d[4]*c[4])ans+=f[tot-d[1]*c[1]-d[2]*c[2]-d[3]*c[3]-d[4]*c[4]];
printf("%lld\n",ans);
}
return 0;
}
2.巨魔没金币(silver.pas/c/cpp)
Time Limit:1s Memory Limit:256MB
【题目描述】
某巨魔去了一趟拍卖行卖东西,赚了不少金币,然后又买了些东西,就没金币了。
该巨魔没了金币,但他还有很多银币。他搞了张圆桌。划分出 2∗N 个位置标号 1 到 2∗N ,然后把 N 个银币放在奇数位置上。接下来每次按如下操作:在任意两个银币之间放上一个银币,然后将原来的银币拿走;所放银币的正反面由它两边的两个银币决定,若两个银币均为正面朝上或反面朝上,则所放银币为正面朝上,否则为反面朝上。 那么操作 T 次之后桌子边缘上银币的情况会是怎样的呢?
【输入格式】
第一行包含两个整数 n 和 T。
接下的一行包含 n 个整 数,表示最开始桌面边缘的硬币摆放情况,第 i 个整数 ai表示第 i 个硬币摆放在 2∗i−1 个位置上, ai=1 表示正面朝上, ai=2 表示反面朝上。
【输出格式】
输出一行 2n 个整数,其中第 i 个整数 bi桌面边缘的第 i 个位置上硬币的情况, bi=1 表示正面朝上, bi=2 表示反面朝上, bi=0 表示没有硬币。
【样例输入输出】
silver.in
10 5
2 2 2 1 1 1 1 1 1 2silver.out
0 1 0 1 0 1 0 1 0 2 0 1 0 2 0 1 0 1 0 1
【数据范围】
30%的数据: 1≤N≤1000 、 1≤T≤1000 。
100%的数据: 1≤N≤100000 、 1≤T≤260 。
【题解】
原题:bzoj1411: [ZJOI2009]硬币游戏
我们可以把每两次操作看成是一次操作,那么每经过一次操作后所有的硬币的位置都不会变化。很容易可以得知每一次操作时的第i个硬币是由上一次操作后的第
i+1
个硬币和第
i−1
个硬币异或而来。那么现在我们来求证在第
2k
次操作时硬币i由初始状态的第
i+2k
和第
i−2k
个硬币异或而来。首先当
k=0
时是满足条件的,那么我们假设当
k=n
时满足条件,则当
k=n+1
时每一个硬币
i
由经过
这样,每次计算
2k
后的状态,我们就可以在
O(nlog2T)
的时间内解决此题。
【代码】
#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int size = 100000+10;
int a[size],b[size],n;
LL m,s,m1;bool flag;
template< typename Type >inline void read( Type &In ){
In=0;char ch=getchar();
for( ;ch> '9'||ch< '0';ch=getchar() );
for( ;ch>='0'&&ch<='9';ch=getchar() )In = In*10 + ch-'0';
}
void init() {
read(n);read(m);
for(int i=1;i<=n;i++)
read(a[i]),--a[i];
}
void work() {
m1=m;
if(m%2==1) {
--m;
for(int i=1;i<=n;i++)
b[i]=a[i]^a[i%(n+1)];
memcpy(a,b,sizeof b);
}
m=m/2;
while(m>0) {
s=(m&-m);
for(int j=1;j<=n;j++)
b[j]=a[((j-s)%(n+n-1))%(n+1)]^a[(j+s-1)%(n+1)];
memcpy(a,b,sizeof b);
m=m-s;
}
flag=true;
for(int i=1;i<=n;i++) {
if(m1%2==1) {
if(flag) {
flag=false;printf("0");
}
else printf(" 0");
}
if(flag) {
printf("%d",a[i]+1);
flag=false;
}
else printf(" %d",a[i]+1);
if(m1%2==0) printf(" 0");
}
}
int main() {
init();work();
return 0;
}
3.守卫部署(Guard.pas/c/cpp)
Time Limit:1s Memory Limit:256MB
【问题描述】
征夷王(征夷大白兔王子青王神人)最近打算灭掉越南(南夷)。征夷王的国家由 N 个城市、N-1 条双向道路组成,其中,任意两个城市之间有且仅有一条通路连接。然而,万恶的日本(东倭)想在此时偷袭征夷王的国家!每当一个城市被袭击时,与之有直接道路相连的城市或该城市中若有一只守卫军队,便可以化险为夷。但是,如果与之有直接道路相连的城市和该城市中没有一个城市有一只守卫军队,那么这个城市就悲剧了。征夷王显然不愿意看到自己的国家侵略。所以他在某些城市部署若干只守卫军队,使得任意一个城市被袭击时均可化险为夷。但是,为了尽可能迅捷地灭掉南夷,征夷王希望部署的守卫军队只数最少。现在伟大的王想知道,他最少要部署多少只守卫军队?
【输入】
输入文件名为 Guard.in。
输入第一行一个正整数 N,代表征夷王国家拥有的城市个数。下接 N-1 行,每行两个正整数 Xi 和 Yi,表示编号为 XI 和 YI 的城市之间有一条双向道路。
【输出】
输出文件名为 Guard.out。
输出第一行一个正整数,代表最少要部署的军队个数。
【输入输出样例】
Guard.in
5
1 3
5 2
4 3
3 5
Guard.out
2
【数据范围】
30%的数据,
1≤N≤10
。
100%的数据,
1≤N≤100000
。
然而并不会…
总结
哎~