week5作业
C-平衡字符串
题目大意
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。
题解
尺取法
当前[L,R]满足条件,L++
当前[L,R]不满足条件,R++
考虑如何判断区间[L,R]替换后是否能满足要求
s
u
m
[
i
]
sum[i]
sum[i]表示第i个字符在字符串中的个数
c
n
t
[
i
]
cnt[i]
cnt[i]表示区间
[
l
,
r
]
[l,r]
[l,r]中第i个字符的个数
k
[
i
]
k[i]
k[i]除去区间
[
l
,
r
]
[l,r]
[l,r]后第i个字符的个数
t
=
m
a
x
(
k
[
0
]
,
k
[
1
]
,
k
[
2
]
,
k
[
3
]
)
t=max(k[0],k[1],k[2],k[3])
t=max(k[0],k[1],k[2],k[3])
t
o
t
=
r
−
l
+
1
tot=r-l+1
tot=r−l+1区间长度
f
=
t
o
t
−
∑
(
t
−
k
[
i
]
)
f=tot-\sum(t-k[i])
f=tot−∑(t−k[i])使所有字符达到t后区间剩余的字符个数
如果
f
>
=
0
f>=0
f>=0且为4的倍数则满足要求。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100003
using namespace std;
char s[N];
int n,cnt[4],sum[4],k[4];
void calc(char ch,int opt,int *a)
{
if(ch=='Q') a[0]+=opt;
if(ch=='W') a[1]+=opt;
if(ch=='E') a[2]+=opt;
if(ch=='R') a[3]+=opt;
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
for (int i=1;i<=n;i++) calc(s[i],1,sum);
if (sum[0]==sum[1]&&sum[1]==sum[2]&&sum[2]==sum[3]) {
printf("0\n");
return 0;
}
int ans=n+1; int l,r;
l=r=1; calc(s[1],1,cnt);
while (l<=r&&r<=n) {
int t=0;
for(int i=0;i<4;i++) k[i]=sum[i]-cnt[i],t=max(t,k[i]);
int tot=r-l+1;
int f=tot;
for(int i=0;i<4;i++) f-=(t-k[i]);
if (f>=0&&f%4==0) ans=min(ans,tot),calc(s[l++],-1,cnt);
else calc(s[++r],1,cnt);
}
printf("%d\n",ans);
return 0;
}
week6作业
A-氪金带冬(HDU2196)
题目大意
求树中每个点到其他点的最长距离。
题解1
树的直径
假定树的直径的两个端点为
(
u
,
v
)
(u,v)
(u,v)
那么这个最长距离的另一端不是u,就是v。所以我们只需从u,v去dfs,然后去更新每个点的答案即可。
#include<cstdio>
#include<iostream>
#include<cstring>
#define N 20003
using namespace std;
int n,tot,point[N],v[N],nxt[N],l[N];
int a[N],ans,ansx;
void add(int x,int y,int k)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; l[tot]=k;
}
int max(int x,int y)
{
return x>y?x:y;
}
void dfs(int x,int fa,int d)
{
a[x]=max(a[x],d);
if (d>ans)
ans=d,ansx=x;
for (int i=point[x];i;i=nxt[i])
if(v[i]!=fa) dfs(v[i],x,d+l[i]);
}
void clear()
{
tot=0;
memset(point,0,sizeof(point));
memset(nxt,0,sizeof(nxt));
memset(a,0,sizeof(a));
}
int main()
{
while (scanf("%d",&n)!=EOF){
clear();
for (int i=2;i<=n;i++) {
int x,y;
scanf("%d%d",&x,&y);
add(i,x,y);
add(x,i,y);
}
ansx=ans=-1;
dfs(1,0,0);
int t=ansx;
ansx=ans=-1;
dfs(t,0,0);
dfs(ansx,0,0);
for(int i=1;i<=n;i++) printf("%d\n",a[i]);
}
return 0;
}
题解2
树形DP
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 20003
using namespace std;
int n,m;
int point[N],nxt[N],v[N],tot,c[N];
int f[N],g[N],maxn[N],_maxn[N],ans[N];
void add(int x,int y,int z)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
int dfs(int x,int fa)//求出最长链和次长链
{
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
int t=c[i]+dfs(v[i],x);
if (t>maxn[x])
_maxn[x]=maxn[x],maxn[x]=t;
else
if (t>_maxn[x])
_maxn[x]=t;
}
return maxn[x];
}
void get_ans(int x,int fa)
{
for (int i=point[x];i;i=nxt[i])
if(v[i]!=fa)
{
f[v[i]]=maxn[v[i]];//子树中的最长距离
if (maxn[x]==maxn[v[i]]+c[i])//如果当前子节点在他的最长链中,只能用次长链和子树外的最长距离更新
g[v[i]]=c[i]+max(_maxn[x],g[x]);
else
g[v[i]]=c[i]+ans[x];
ans[v[i]]=max(g[v[i]],f[v[i]]);
get_ans(v[i],x);
}
}
int main()
{
while (scanf("%d",&n)!=EOF)
{
tot=0;
memset(nxt,0,sizeof(nxt));
memset(point,0,sizeof(point));
for (int i=2;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(i,x,y);
}
for (int i=1;i<=n;i++)
f[i]=0,g[i]=0,maxn[i]=0,_maxn[i]=0;
dfs(1,0);
ans[1]=f[1]=maxn[1];
get_ans(1,0);
for (int i=1;i<=n;i++)
printf("%d\n",ans[i]);
}
}
week6限时模拟
A-掌握魔法の东东II
题目大意
有 A × B 张扑克牌。每张扑克牌有一个大小(整数,记为a,范围区间是 0 到 A - 1)和一个花色(整数,记为b,范围区间是 0 到 B - 1。
9种牌型如下(匹配时低序号优先)
1.同花顺: 同时满足规则 5 和规则 4.
2.炸弹 : 5张牌其中有4张牌的大小相等.
3.三带二 : 5张牌其中有3张牌的大小相等,且另外2张牌的大小也相等.
4.同花 : 5张牌都是相同花色的.
5.顺子 : 5张牌的大小形如 x, x + 1, x + 2, x + 3, x + 4
6.三条: 5张牌其中有3张牌的大小相等.
7.两对: 5张牌其中有2张牌的大小相等,且另外3张牌中2张牌的大小相等.
8.一对: 5张牌其中有2张牌的大小相等.
9.要不起: 这手牌不满足上述的牌型中任意一个.
从A × B 张扑克牌中拿走了 2 张牌!分别是 (a1, b1) 和 (a2, b2). (其中a表示大小,b表示花色。现在要从剩下的扑克牌中再随机拿出 3 张!组成一手牌,求九种牌型的方案数
题解
模拟
直接dfs枚举所有可能性再进行判定即可,具体实现见代码。
c
n
t
[
i
]
cnt[i]
cnt[i]表示大小为
i
i
i的牌的数量
s
u
m
[
i
]
sum[i]
sum[i]表示数量为
i
i
i的牌组数
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct data{
int x,y;
data(int _x=0,int _y=0) {
x=_x; y=_y;
}
friend bool operator ==(data a,data b){
return a.x==b.x&&a.y==b.y;
}
}a[6],b[6];
int n,m,sum[10],ans[10],cnt[30],tot;
int a1,b1,a2,b2;
bool cmp(data a,data b){
return a.x<b.x||a.x==b.x&&a.y<b.y;
}
bool check_4()
{
int t=a[0].y;
for (int i=1;i<5;i++)
if(a[i].y!=t) return false;
return true;
}
bool check_5()
{
for (int i=1;i<5;i++)
if(a[i-1].x+1!=a[i].x) return false;
return true;
}
void solve()
{
for(int i=0;i<5;i++) b[i]=a[i];
sort(a,a+5,cmp);
for(int i=0;i<5;i++){
int t= cnt[a[i].x];
if (t) sum[t]--;
cnt[a[i].x]++;
sum[t+1]++;
}
if(check_5()&&check_4()) ans[1]++;
else if(sum[4]==1) ans[2]++;
else if(sum[3]==1&&sum[2]==1) ans[3]++;
else if(check_4()) ans[4]++;
else if(check_5()) ans[5]++;
else if(sum[3]==1&&sum[1]==2) ans[6]++;
else if(sum[2]==2) ans[7]++;
else if(sum[2]==1&&sum[1]==3) ans[8]++;
else ans[9]++;
for(int i=0;i<5;i++) cnt[a[i].x]--;
for(int i=0;i<5;i++) sum[i]=0;
for (int i=0;i<5;i++) a[i]=b[i];
}
void dfs(int x,int l_a)
{
if (x==5) {
solve();
return;
}
for (int i=l_a;i<n*m;i++){
data t=data(i/m,i%m);
if (t==a[0]||t==a[1]) continue;
a[x]=t;
dfs(x+1,i+1);
}
}
int main()
{
scanf("%d%d",&n,&m);
scanf("%d%d",&a1,&b1);
a[0]=data(a1,b1);
scanf("%d%d",&a2,&b2);
a[1]=data(a2,b2);
dfs(2,0);
for (int i=1;i<=9;i++) printf("%d ",ans[i]);
printf("\n");
return 0;
}
week7月模拟题
CSP201604-3 路径解析
题目大意
对于每个路径,给出正规化以后的形式。一个路径经过正规化操作后,其指定的文件不变,但是会变成一个不包含 . 和 … 的绝对路径,且不包含连续多个 / 符号。如果一个路径以 / 结尾,那么它代表的一定是一个目录,正规化操作要去掉结尾的 /。若这个路径代表根目录,则正规化操作的结果是 /。若路径为空字符串,则正规化操作的结果是当前目录。
题解
模拟
大致的思路就是对于不以"/“开头的相对路径,直接加上当前目录即可,绝对路径不作处理,然后根据”/“把路径拆分出来,依次压入vector,对于拆分出的结构,如果为”…“表示回到上级目录;直接把vector的最后一个元素弹出即可,如果为”.“或者空串直接忽略;否则压入vector。最后输出即可。
特别的用到了
s
t
r
i
n
g
s
t
r
e
a
m
stringstream
stringstream字符流,
s
t
r
i
n
g
s
t
r
e
a
m
stringstream
stringstream与
g
e
t
l
i
n
e
getline
getline合用真的可以省去很多繁琐的操作,直接就可以根据”/"来拆分字符串。
g
e
t
l
i
n
e
getline
getline的这个用法有点类似
s
s
c
a
n
f
sscanf
sscanf。
需要特别注意的一点是路径可能为空字符串,所以在读入的时候不能用cin,要用getline。
#include<bits/stdc++.h>
using namespace std;
string str,str2;
vector<string> v;
int n;
void print()
{
for(int i=0;i<v.size();i++) cout<<"/"<<v[i];
if(v.empty()) printf("/");
printf("\n");
}
void solve(string str)
{
stringstream s(str);
string now;
while (getline(s,now,'/')) {
if(now.empty()) continue;
if(now=="..") {
if(!v.empty()) v.pop_back();
}
else if(now!=".") v.push_back(now);
}
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d",&n);
getchar();
getline(cin,str);
while(n--) {
getline(cin,str2);
if(str2[0]!='/') str2=str+"/"+str2;
v.clear();
solve(str2);
print();
}
return 0;
}