时间安排
8:00—8:10 读题:T1应该是区间dp,但是小细节比较多;T2标题提示的很明显了,是个背包问题(感觉比较简单准备放到最后再做);T3看到的第一眼没什么思路,先做一道题如果还有时间再想想
8:10—8:40 码T1,结果发现普通的dp套路做不来,写了个废掉的代码
8:40—9:00 又理解了一下T3,简单码了一下发现输出一直是-1.放弃准备磕T1
9:00—9:30 想到了T1一种新的状态,感觉可行,疯狂码ing
9:30—9:50 手造了两组数据,调T1
9:50 突然说要延时20分钟
9:50—10:10 终于过了手造数据
10:10 发现来不及码T2了只好匆忙写了个T3的骗分,暴力也没写完
最终得分:100+0+2=102
题目分析
T1 格斗俱乐部
题目描述
格斗俱乐部是格斗爱好者的一个组织,在这里,格斗者们能通过与别的成员进行格斗来释放自己的压力
与轻松自己的情绪。
最近俱乐部举行了一场比赛,该比赛有N位选手参加,他们将围成一个圆圈,每一场比赛圈内任意的两
位相邻的选手均可进行相互的格斗,胜利者将留在圈内进入下轮比赛而失败者则直接被送往医院(没有
平局)。比赛是残酷的,最后圈内将只剩下一位选手,他将是总冠军。
我们做个奇怪的假设,两位选手进行格斗,他们比赛的结果总是确定的。虽然俱乐部的成员们都很喜欢
格斗,但是他们仍然很希望能获得总冠军。
现在你通过统计已经知道了任意两位选手格斗的结果,你有责任告诉每位选手,如果赛程合适安排的
话,他是否可能成为总冠军。
输入格式
数据第一行是一个整数 N,((1<=N<=40)),表示比赛的选手数量。
接下来给出一个 N*N的“ 0”、“1 ”矩阵 A(行内用空格隔开),第i行第j列为 1表示选手i能战胜选手j,
否则选手j能战胜选手i。
你可以假定
A
i
j
Aij
Aij与
A
j
i
Aji
Aji(i≠j)均是不同的且
A
i
i
Aii
Aii=0。比赛开始时所有选手按顺时针方向由编号 到编
号 站成一个圈,初始时编号 与编号 的选手是相邻的。
输出格式
输出包含 行,每行为一个整数“0”或“1”,“1”表示第i号选手有可能成为冠军,“0”表示不可能。
input
3
0 1 1
0 0 1
0 0 0
output
1
0
0
分析:判断x是否能成为冠军,开一个链,x点拆成两个,那编号为x的人能成为冠军的条件是他能和自己相邻。这样,在连续几个人的链中,只须考虑头尾两个人能否相邻,中间的则不予考虑。设f[i,j]记录i和j能否相邻,能则为1,则问题转化为了找到一个k,使得i和k,k和j均能相邻,而i或者j能打败k。
#include<bits/stdc++.h>
using namespace std;
#define xyg(i,j,k) for(int i=j;i<=k;i++)
const int maxn=1010;
int n;
int s[maxn];
int f[maxn][2*maxn],g[maxn][maxn];
int main()
{
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
scanf("%d",&n);
xyg(i,1,n)
xyg(j,1,n)
scanf("%d",&g[i][j]);
xyg(i,1,n*2)
{
f[i][i+1]=1; //第i个人和第i+1个人开始一定是相邻的
s[i]=i-(i>n)*n;
}
xyg(x,1,n)
{
for(int i=1;x+i<=n*2;i++)
{
int j=x+i;
xyg(k,i+1,j-1)
{
if(f[i][k]&&f[k][j])
{
if(g[s[i]][s[k]]||g[s[j]][s[k]]) //i能打败k或者j能打败k都能使i,j相邻
{
f[i][j]=1;
break;
}
}
}
}
}
xyg(i,1,n) cout<<f[i][i+n]<<endl;
return 0;
}
T2 背包问题
Description
从T组物品中选出一些物品,放入背包中,求剩余空间的最小值。
限制条件:从每组物品中挑选物品必须要选取连续的一段。就是说,如果这组物品共有n个: 物品1、物品2、物品3、…、物品n,那么只能选取物品i、物品i+1、…、物品j,其中1<=i<=j<=n,或者不选。
Input
第一行为两个用空格隔开的正整数v和T。表示背包的空间和物品的组数。接下来有T行,每行先是一个正整数ni,表示这组物品有ni个,然后ni个正整数,表示每个物品的大小。
Output
仅一个数,表示剩余空间的最小值。
Sample Input
100 3
3 7 6 8
2 80 70
4 101 108 103 150
Sample Output
6
【样例说明】
第1组选6、8,第2组选80,第3组不选。
【限制】
60%的数据满足:1 <= ni <= 10
100%的数据满足:1 <= ni <= 100,1<=v<=5000,1<=T<=10
分析:
每组物品,只能选连续的一段且只能选一次,所以我们可以想到DP.
b
[
i
,
j
]
b[i,j]
b[i,j]表示第i组物品,是否出现连续一段总体积为j的情况。
f
[
i
,
j
]
f[i,j]
f[i,j]表示前T组物品,用了j体积装物品,能装载的物品的最大值。
用一个前缀和去统计,然后暴力去枚举判断就好了。
然后状态转移方程:
f
[
i
,
j
]
:
=
m
a
x
(
f
[
i
,
j
−
1
]
,
m
a
x
(
f
[
i
−
1
,
j
−
k
]
+
k
)
)
f[i,j]:=max(f[i,j-1],max(f[i-1,j-k]+k))
f[i,j]:=max(f[i,j−1],max(f[i−1,j−k]+k));
最后在这里面找出一个最大值,然后用
v
−
m
a
x
v-max
v−max。
#include<bits/stdc++.h>
using namespace std;
const int maxn=110;
#define xyg(i,j,k) for(int i=j;i<=k;i++)
int v,t;
int a[maxn][maxn],sum[maxn][maxn],r[maxn][100010];
int f[5000],tot[maxn],n[maxn];
int main()
{
freopen("bag.in","r",stdin);
freopen("bag.out","w",stdout);
cin>>v>>t;
memset(f,-10,sizeof(f));
f[0]=0;
xyg(i,1,t)
{
scanf("%d",&n[i]);
xyg(j,1,n[i])
scanf("%d",&a[i][j]);
}
xyg(i,1,t)
xyg(j,1,n[i])
sum[i][j]=sum[i][j-1]+a[i][j];
xyg(i,1,t)
{
xyg(j,1,n[i])
xyg(k,1,j)
r[i][++tot[i]]=sum[i][j]-sum[i][k-1];
r[i][++tot[i]]=0;
}
xyg(i,1,t)
for(int j=v;j>=0;j--)
xyg(k,1,tot[i])
if(j>=r[i][k])
f[j]=max(f[j],f[j-r[i][k]]+r[i][k]);
for(int i=v;i>=0;i--)
{
if(f[i]>0)
{
cout<<v-f[i];
break;
}
}
return 0;
}
T3 【atcoder abc142E】Get Everything
Description:
有n个野怪,给定m个武器的价钱和它能打的野怪,求打完所有野怪要的最少花费
输入格式
第一行输入野怪的个数 n 和武器的个数 m
接下来共有m组,第i组描述第i把武器的信息:
对于每一组武器:
第一行输入一个a和b, 分别代表这种武器的价钱和这把武器能打死几个野怪
第二行输入b个编号,代表这个技能能打死野怪的编号
输出格式
输出最小的花费打死所有的野怪. 如果不能全部打死,输出-1
样例数据
input
2 3
10 1
1
15 1
2
30 2
1 2
output
25
购买第一把武器和第二把武器可以打死所有的野怪,共花费最小消耗25
分析
宝箱的数量较小,可以用2进制状态压缩来表示每个宝箱持有的状态。
例如1号宝箱就是00000001,2号就是00000010…依次往后。
现在假设你当前钥匙能开的宝箱的二进制是now,当前的钥匙为i ii号钥匙
定义
d
p
(
b
i
t
)
dp(bit)
dp(bit)为该二进制下解开这些宝箱所需要的最少钱数
那么此时
d
p
(
b
i
t
∣
n
o
w
)
=
m
i
n
(
d
p
(
b
i
t
∣
n
o
w
,
d
p
(
b
i
t
)
+
a
[
i
]
)
dp(bit|now)=min(dp(bit|now,dp(bit)+a[i])
dp(bit∣now)=min(dp(bit∣now,dp(bit)+a[i])
初始状态
d
p
(
0
)
=
0
dp(0)=0
dp(0)=0其他的状态初始化为inf,最终答案就是
d
p
(
(
1
<
<
N
)
−
1
)
dp ( ( 1 < < N ) − 1 )
dp((1<<N)−1) 的值
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int n,m;
int a[1111], b[1111];
int c[1111][20];
vector<int> dp(5050, inf);
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
cin>>n>>m;
for (int i=1;i<=m;i++)
{
cin>>a[i]>>b[i];
for(int j=1;j<=b[i];j++) cin>>c[i][j];
}
dp[0]=0;
for (int i=1;i<=m;i++)
{
int now=0;
for(int j=1;j<=b[i];j++)
now|=(1<<(c[i][j]-1));
for(int bit=0;bit<(1<<n);bit++)
dp[bit|now]=min(dp[bit|now],dp[bit]+a[i]);
}
if(dp[(1<<n)-1]==inf) cout<<-1<<endl;
else cout<<dp[(1<<n)-1]<<endl;
return 0;
}
总结&反思
下次先把简单的做了,不至于到最后磕一道题导致没时间打暴力