所以我又回来了,这次是test7。
(test1,test2,test3,集体失踪,敬请期待(摊手))
嗯,喜大普奔。
上题。
1.1807
题目描述
给出一个由数字(‘0’-‘9’)构成的字符串。我们说一个子序列是好的,如果他的每一位都是 1、8、0、7 ,并且这四个数字按照这种顺序出现,且每个数字都出现至少一次(111888888880000007 是好的,而 1087 不是)。请求出最大的好的子序列的长度。
输入格式
输入唯一一行一个字符串。
输出格式
一行一个整数表示答案。
样例数据
输入
1800777700088888000777
输出
13
备注
【数据范围】
对 30% 的输入数据 :字符串长度≤100 ;
对 100% 的输入数据 :字符串长度≤1000000 。
第一次看到题的时候纠结了好久,感觉怎么做都考虑不完全。
最后决定把‘1’赋为1,‘8’赋为2,‘0’赋为3,‘7’赋为4。
然后用了最长不下降子序列。
但是bug简直太多了。
首先不仅2大于1,3也大于1,4也是。
然后又不能保证1、2、3、4四个数字都会出现。
于是又在不同情况下做了分类讨论,讨论出来3kb。
然后……然后只有10分。
这是一个黄少天听了沉默,韩文清看了流泪的故事。
好了,代码和正解。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
int len,ans;
int a[1000005],f[1000005][5];
char s[1000005];
int main()
{
//freopen("1807.in","r",stdin);
//freopen("1807.out","w",stdout);
memset(f,128,sizeof(f));
scanf("%s",s+1);
len=strlen(s+1);
f[0][0]=0;
for(int i=1;i<=len;i++)
{
f[i][0]=f[i-1][0];
f[i][1]=f[i-1][1];
f[i][2]=f[i-1][2];
f[i][3]=f[i-1][3];
f[i][4]=f[i-1][4];
if(s[i]=='1')
f[i][1]=max(f[i][1],f[i-1][0])+1;
if(s[i]=='8')
f[i][2]=max(f[i][2],f[i-1][1])+1;
if(s[i]=='0')
f[i][3]=max(f[i][3],f[i-1][2])+1;
if(s[i]=='7')
f[i][4]=max(f[i][4],f[i-1][3])+1;
}
cout<<max(0,f[len][4])<<endl;
return 0;
}
开一个二维数组(其实一维也可以,为了叙述方便)f[i][j],i表示现在已经取了前面i位,用到了第j个数字(即1、8、0、7),最后输出f[n][4]。
至于动态转移方程,e.g.对于‘8’来说,只有前面是‘1’或是‘8’时才有更新的资格,否则没有。
2.Minimum
题目描述
给出一幅由 n 个点 m 条边构成的无向带权图。
其中有些点是黑点,另外点是白点。
现在每个白点都要与他距离最近的黑点通过最短路连接(如果有很多个,可以选取其中任意一个),我们想要使得花费的代价最小。请问这个最小代价是多少?
注意:最后选出的边保证每个白点到黑点的距离任然是最短距离。
输入格式
第一行两个整数 n,m ;
第二行 n 个整数,0 表示白点,1 表示黑点;
接下来 m 行,每行三个整数 x,y,z ,表示一条连接 x 和 y 点,权值为 z 的边。
输出格式
如果无解,输出“impossible”,否则,输出最小代价。
样例数据
输入
5 7
0 1 0 1 0
1 2 11
1 3 1
1 5 17
2 3 1
3 5 18
4 5 3
2 4 5
输出
5
备注
【样例说明】
选 2、4、6 三条边。
【数据范围】
对 30% 的输入数据 :1≤n≤10,1≤m≤20;
对 100% 的输入数据 :1≤n≤100000,1≤m≤200000,1≤z≤1000000000 。
嗯很好,其实第一遍看的时候根本没有理解题目的意思。
好吧最后还是懂了,就是找一个白点到周围黑点的最短路,若有多个全部相加。(然而懂了也并没有什么用)【滑稽】
直接说说考完后拿到的正解吧。
整个解题过程分为两块,但前提是还要证明两条规律。
1.最短路上的所有点必连通,且一条最短路上只有最末点是黑点,其余皆为白点。
2.每个点连在最短路上都只连一次,即一个白点到黑点的最短路只有一条。
所以整个问题就分为先用各种算法(喜欢就好)求最短路,然后再建最小生成树,注意能连通的尽量都要连通。
具体做法可以取一个超级点S,在S与每个黑点之间连权值为0的边(可以理解为把所有的黑点都变成起点),先处理从S出发到每个点的最短距离,获得最短路径图一张。这样以后我们需要取权值最小的边的集合使这幅图连通,此时用到Kruskal算法求最小生成树。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#define S 0
using namespace std;
struct node
{
int next;
int to;
int w;
}bian[3000003];
struct gl
{
int x;
int y;
int w;
}e[3000003];
int n,m,tot,cnt,inf,cont;
long long ans,dis[100010];
int first[100010],b[3000003],c[100010];
bool exist[100010];
inline void create(int x,int y,int z)
{
tot++;
bian[tot].next=first[x];
first[x]=tot;
bian[tot].to=y;
bian[tot].w=z;
}
inline void czh(int x,int y,int z)
{
e[++cnt].x=x;
e[cnt].y=y;
e[cnt].w=z;
}
inline void zql(int s)
{
memset(dis,40,sizeof(dis));
inf=dis[0];
int head=0,tail=1;
dis[s]=0;
b[1]=s;
exist[s]=true;
while(head^tail)
{
if(++head==3000000)
head=1;
int now=b[head];
exist[now]=false;
for(int i=first[now];i;i=bian[i].next)
{
int next=bian[i].to;
if(dis[next]>dis[now]+bian[i].w)
{
dis[next]=dis[now]+bian[i].w;
if(!exist[next])
{
exist[next]=true;
if(++tail==3000000)
tail=1;
b[tail]=next;
}
}
}
}
}
inline int find(int x)
{
return x==c[x]?x:c[x]=find(c[x]);
}
inline bool comp(gl a,gl b)
{
return a.w<b.w;
}
int main()
{
//freopen("minimum.in","r",stdin);
//freopen("minimum.out","w",stdout);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int u;
cin>>u;
if(u==1)
create(S,i,0);
}
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
create(x,y,z);
create(y,x,z);
}
zql(S);
for(int i=1;i<=n;i++)
if(dis[i]==inf)
{
cout<<"impossible"<<endl;
return 0;
}
for(int i=1;i<=n;i++)
for(int j=first[i];j;j=bian[j].next)
{
if(dis[bian[j].to]+bian[j].w==dis[i])
czh(bian[j].to,i,bian[j].w);
}
sort(e+1,e+1+cnt,comp);
for(int i=1;i<=n;i++)
c[i]=i;
for(int i=1;i<=cnt;i++)
{
int u=e[i].x,v=e[i].y,w=e[i].w;
int pu=find(u),pv=find(v);
if(pu!=pv)
{
c[pu]=pv;
ans+=w;
if(++cont==n-1)
break;
}
}
if(ans)
cout<<ans<<endl;
else
cout<<"impossible"<<endl;
return 0;
}
3.(假装这里有题的样子)
好吧其实是真有的,但是莫比乌斯函数什么的数论那一坨还没有过多涉猎,基本上看着题解也是一脸大写的懵逼,所以就装作什么都不知道的样子,假装只有两道题,嗯咳。。。。。
来自2017.7.17
——我认为return 0,是一个时代的终结。