week11作业
F-东东开车了
题目大意
车内提供了 n 张CD唱片,已知东东开车的时间是 n 分钟,找到最能消磨时间的唱片数量,并按使用顺序输出答案
假设:
CD数量不超过20张
没有一张CD唱片超过 N 分钟
每张唱片只能听一次(且如果开始听则必须听完)
唱片的播放长度为整数
N 也是整数
题解
背包DP
f
[
i
]
f[i]
f[i]表示能否恰好i分钟
g
[
i
]
g[i]
g[i]表示恰好i分钟所选择的最后一张CD的编号
然后就可以根据
g
g
g数组倒推找到答案
另外这道题因为CD的数量很少,所以其实可以直接暴力实现。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 200000
using namespace std;
const int inf=1e9;
int f[N],a[N],g[N],v,n,cnt;
int main()
{
while (scanf("%d%d",&v,&n)!=EOF) {
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=0;i<=v;i++) f[i]=0,g[i]=0;
f[0]=1;
for(int i=1;i<=n;i++)
for(int j=v;j>=a[i];j--)
if(f[j-a[i]]&&!f[j]) f[j]|=f[j-a[i]],g[j]=i;
vector<int> ans; ans.clear();
int m=0;
for (int i=v;i>=0;i--)
if (f[i]) {
m=i;
break;
}
int t=g[m],sum=m;
while(sum) {
ans.push_back(a[t]);
sum-=a[t];
t=g[sum];
}
for (int i=ans.size()-1;i>=0;i--) printf("%d ",ans[i]);
printf("sum:%d\n",m);
}
return 0;
}
week12作业
E-选做题-2
题目大意
有n个作业,完成某个作业需要一定的时间,而且每个作业有一个截止时间,若超过截止时间,一天就要扣一分。求使得扣的分数最少的安排
Tips: 如果开始做某个作业,就必须把这个作业做完了,才能做下一个作业。
若存在多个安排方式扣分相同,输出字典序最小的方案
题解
状态压缩DP
f
[
S
]
f[S]
f[S]表示完成S作业集合后被扣的最小分数
p
r
e
[
S
]
pre[S]
pre[S]记录一下到达当前状态的完成的最后一个作业,用于倒退出作业完成顺序
状态转移方程为
f
[
S
∣
(
1
<
<
x
)
]
=
m
i
n
(
f
[
S
∣
(
1
<
<
x
)
]
,
f
[
S
]
+
t
m
p
)
f[S|(1<<x)]=min(f[S|(1<<x)],f[S]+tmp)
f[S∣(1<<x)]=min(f[S∣(1<<x)],f[S]+tmp)
其中
t
m
p
=
m
a
x
(
0
,
S
作
业
集
合
完
成
对
应
的
时
间
+
作
业
x
完
成
所
需
时
间
−
作
业
x
的
D
D
L
)
tmp=max(0,S作业集合完成对应的时间+作业x完成所需时间-作业x的DDL)
tmp=max(0,S作业集合完成对应的时间+作业x完成所需时间−作业x的DDL)
这道题的关键其实是如何保证答案的字典序最小
(1)输入保证作业按照字典序排序
(2)状态S越小则其对应的字典序越小
(3)S从小到大枚举,x从小到大枚举,只有
f
[
S
]
+
t
m
p
f[S]+tmp
f[S]+tmp严格小于的时候才更新
p
r
e
[
S
]
pre[S]
pre[S],即可保证字典序
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#define N 32768
using namespace std;
const int inf=1e9;
struct data{
string s;
int d,c;
}v[20];
int f[N],pre[N],n;
int calc(int x)
{
int cnt=0;
for (int i=0;i<n;i++)
if ((x>>i)&1) cnt+=v[i].c;
return cnt;
}
int main()
{
int t; scanf("%d",&t);
while (t--) {
scanf("%d",&n);
for (int i=0;i<n;i++) cin>>v[i].s>>v[i].d>>v[i].c;
memset(pre,0,sizeof(pre));
for(int i=0;i<N;i++) f[i]=inf;
f[0]=0;
for (int i=0;i<(1<<n)-1;i++) {
int sum=calc(i);
for (int j=0;j<n;j++){
if ((i>>j)&1) continue;
int t=f[i]+max(sum+v[j].c-v[j].d,0);
if (t<f[i|(1<<j)]) {
f[i|(1<<j)]=t;
pre[i|(1<<j)]=j;
}
}
}
int num=(1<<n)-1;
printf("%d\n",f[num]);
vector<int> ans; ans.clear();
while (num) {
ans.push_back(pre[num]);
num-=(1<<pre[num]);
}
for (int i=ans.size()-1;i>=0;i--) cout<<v[ans[i]].s<<endl;
}
return 0;
}
CSP-M3
C-咕咕东学英语
题目大意
查询有多少个子串满足条件
条件为子串中的每一个字符都属于一个大于1的回文串中
题解
这道题直接求满足条件的子串不容易,所以我们求反,即答案=子串总数-不满足条件的子串
首先字符串中只包含AB两种字符,所以字符串被砍成了好多节
AA|BBB|AAAAA|BB…
如果子串的首尾在同一节中,且长度大于1,则一定满足条件
如果子串跨越了3个或3个以上的节,那么一定满足条件
所以不满足条件的子串其实就是首尾处在相邻两个节中且在其中一节中的长度为1的子串
ABBBB…
BAAAA…
…AAAAB
…BBBBA
那这样很容易就可以求出不满足的答案,需要注意“AB”这种不要重复计算。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define N 300003
#define LL long long
using namespace std;
int n;
char s[N];
vector<int> v;
int main()
{
scanf("%d",&n);
scanf("%s",s);
int t=1;
LL ans=(LL)n*(n+1)/2;
ans-=n;//长度1
for (int i=1;i<n;i++)
if (s[i]!=s[i-1]) v.push_back(t),t=1;
else t++;
v.push_back(t);
if(v.size()>1) {
for (int i=0;i<v.size();i++)
if(i==0||i==v.size()-1) ans-=v[i];
else ans-=(2*v[i]-1);
ans++;
}
cout<<ans<<endl;
return 0;
}
月模拟题
CSP201809-3元素选择器
题目大意
题解
模拟
不难看出这个结构化文档,其实是一个树形结构,所以对于每个节点我们存储他的标签、id和所在的层数。
对于标签选择器和id选择器,我们只需要枚举所有的节点,找到匹配的即可输出答案,单次查询的时间复杂度为
O
(
n
)
O(n)
O(n)
对于后代选择器,显然需要在树上进行匹配,我们存储的顺序就是读入的顺序,那么如何找到每个点的父节点呢?设当前节点的层数为
l
e
v
e
l
level
level,其实每个点的父节点就是从当前点向前遍历找到的第一个层数为
l
e
v
e
l
−
1
level-1
level−1的节点。所以我们可以从每个节点开始,从下到上依次进行匹配,即若当前节点匹配上了选择器的最后一个元素,那么我们找搜寻当前节点到根的路径看是否可以匹配选择器中元素。匹配原则能匹配则匹配,即我们倒序进行匹配,如果当前点和父节点都能匹配当前应该匹配的元素,那么我们选择当前点进行匹配。单次查询的时间复杂度
O
(
n
2
)
O(n^{2})
O(n2)
#include<bits/stdc++.h>
#define N 103
using namespace std;
vector<int> ans;
vector<string> v;
struct data{
string tag,id;
int x,level;
}a[N];
void change(string &ss)
{
for (int i=0;i<ss.size();i++)
if (ss[i]>='A'&&ss[i]<='Z') ss[i]='a'+ss[i]-'A';
}
void print()
{
printf("%d ",ans.size());
for (int i=0;i<ans.size();i++) printf("%d ",ans[i]);
printf("\n");
}
bool match(string s,int x)
{
if(s[0]=='#') {
s=s.substr(1);
if (a[x].id!=s) return false;
}
else {
change(s);
if (a[x].tag!=s) return false;
}
return true;
}
bool solve(int x)
{
int lv=a[x].level-1; int t=v.size()-1;
string s;
if (!match(v[t],x)) return false;
t--;
for (int i=x-1;i>=1;i--)
if (a[i].level==lv) {
if (match(v[t],i)) t--;
lv--;
if (t<0) return true;
}
return false;
}
int main()
{
freopen("a.in","r",stdin);
int n,q; string s;
scanf("%d%d",&n,&q); getchar();
for (int i=1;i<=n;i++) {
getline(cin,s);
int len=s.size();
a[i].x=i;
int pos=0;
for (int j=0;j<len;j++)
if (s[j]=='.') pos++;
a[i].level=pos/2;
int t1=s.find(" ");
int t2=s.find("#");
if (t2==-1) {
a[i].tag=s.substr(pos);
a[i].id=" ";
}
else {
a[i].tag=s.substr(pos,t1-pos);
a[i].id=s.substr(t2+1);
}
change(a[i].tag);
}
while (q--) {
ans.clear();
getline(cin,s);
int t=s.find(" ");
if (t==-1) {
if (s[0]=='#') {
s=s.substr(1);
for (int i=1;i<=n;i++)
if (a[i].id==s) ans.push_back(i);
print();
}
else {
change(s);
for (int i=1;i<=n;i++)
if (a[i].tag==s) ans.push_back(i);
print();
}
}
else {
v.clear(); string now; s=s+" ";
stringstream ss(s);
while (getline(ss,now,' ')) v.push_back(now);
for (int i=1;i<=n;i++)
if (solve(i)) ans.push_back(i);
print();
}
}
return 0;
}