NOIP2017模拟(7.17)

T1:1807

题目描述

给出一个由数字(‘0’-‘9’)构成的字符串。我们说一个子序列是好的,如果他的每一位都是 1、8、0、7 ,并且这四个数字按照这种顺序出现,且每个数字都出现至少一次(111888888880000007 是好的,而 1087 不是)。请求出最大的好的子序列的长度。

输入格式

输入唯一一行一个字符串。

输出格式

一行一个整数表示答案。

分析:

考虑DP,用f[i][j]表示到了第i位,最后一位是j(j=1,8,0,7)的子序列的最长长度(不一定是完整的子序列),当j的必要前导数结尾的最长子序列不为零(即f[i][g(j)]!=0),转移更新f[i][j]=max(f[i-1][j]+1,f[i-1][g(j)]+1),1比较特殊,直接用f[i][0]=f[i-1][0]+1转移,注意当数字不为1,8,0,7时,状态也要进行转移。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
int f[1000010][5];
char s[1000010];
int main()
{
	scanf("%s",s+1);
	int len=strlen(s+1);
	for(int i=1;i<=len;++i)
	{
		if(s[i]=='1')
		{
			f[i][0]=f[i-1][0]+1;
			f[i][1]=f[i-1][1];
			f[i][2]=f[i-1][2];
			f[i][3]=f[i-1][3];
		}
        else
		if(s[i]=='8')
		{
			if(f[i-1][0])
			  f[i][1]=max(f[i-1][1]+1,f[i-1][0]+1);
			else f[i][1]=f[i-1][1];
			f[i][0]=f[i-1][0];
			f[i][2]=f[i-1][2];
			f[i][3]=f[i-1][3];
		}
        else
		if(s[i]=='0')
		{
			if(f[i-1][1])
			  f[i][2]=max(f[i-1][2]+1,f[i-1][1]+1);
			else f[i][2]=f[i-1][2];
			f[i][1]=f[i-1][1];
			f[i][0]=f[i-1][0];
			f[i][3]=f[i-1][3];
		}
        else
		if(s[i]=='7')
		{
			if(f[i-1][2])
			  f[i][3]=max(f[i-1][2]+1,f[i-1][3]+1);
			else f[i][3]=f[i-1][3];
			f[i][1]=f[i-1][1];
			f[i][2]=f[i-1][2];
			f[i][0]=f[i-1][0];
		}
        else
        {
        	f[i][1]=f[i-1][1];
			f[i][2]=f[i-1][2];
			f[i][0]=f[i-1][0];
            f[i][3]=f[i-1][3];
        }
	}
	cout<<f[len][3]<<endl;
	return 0;
}
T2: minimum

题目描述

给出一幅由 n 个点 m 条边构成的无向带权图。
其中有些点是黑点,另外点是白点。
现在每个白点都要与他距离最近的所有黑点通过最短路连接(与所有最近的黑点连接,但必须用最短路上的边),我们想要使得花费的代价最小。请问这个最小代价是多少?
注意:最后选出的边保证每个白点到黑点的距离任然是最短距离。

输入格式

第一行两个整数 n,m ;
第二行 n 个整数,0 表示白点,1 表示黑点;
接下来 m 行,每行三个整数 x,y,z ,表示一条连接 x 和 y 点,权值为 z 的边。

输出格式

如果无解,输出“impossible”,否则,输出最小代价。

分析:

   建一个超级点与所有的黑点以权值为0的边相连,从超级点开始跑最短路,再做一个类似最小生成树的东西,一条边除了权值小之外还要满足dis[edge[i].to]==dis[x]+edge[i].w,即这条边是最短路上的边,最后得出ans。(这不一定要使所有点联通)

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
const int N=200010;
int n,m;
struct node{
	int to,next,u;
	long long w;
}edge[N*4];
int first[N],stack[N*4],cnt,p[N],fa[N];
long long dis[N];
bool vis[N],hei[N];
int read()
{
	int x=0;
	char ch;
	int f=1;
	for(ch=getchar();ch!='-'&&(ch<'0'||ch>'9');ch=getchar());
	if(ch=='-')
	{
		f=-1;
		ch=getchar();
	}
	for(;ch>='0'&&ch<='9';ch=getchar())
	  x=(x<<3)+(x<<1)+ch-'0';
	return x*f;
}
void add(int x,int y,int z)
{
	edge[++cnt].to=y;
	edge[cnt].next=first[x];
	first[x]=cnt;
	edge[cnt].w=z;
	edge[cnt].u=x;
}
void spfa()
{
	memset(dis,127,sizeof(dis));
	int head=0;
	int tail=1;
	dis[0]=0;
	vis[0]=true;
	stack[1]=0;
	while(head<tail)
	{
		head++;
		int x=stack[head];
		vis[x]=false;
		for(int i=first[x];i;i=edge[i].next)
		{
			if(dis[edge[i].to]>dis[x]+edge[i].w)
			{
				dis[edge[i].to]=dis[x]+edge[i].w;
				if(!vis[edge[i].to])
				{
					tail++;
					vis[edge[i].to]=true;
					stack[tail]=edge[i].to;
				}
			}
		}
	}
}
bool comp(node x,node y)
{
	return x.w<y.w;
}
int search(int x)
{
	if(x==fa[x]) return x;
	fa[x]=search(fa[x]);
	return fa[x];
}
int main()
{
	n=read();
	m=read();
	int top=0;
	for(int i=1;i<=n;++i)
	{
		int x=read();
		if(x==1) 
		{
			p[++top]=i;
			hei[i]=true;
		}
	}
	for(int i=1;i<=m;++i)
	{
		int x,y,z;
		x=read();
		y=read();
		z=read();
		add(x,y,z);
		add(y,x,z);
		if(hei[x]==true||hei[y]==true)
		{
			hei[x]=true;
			hei[y]=true;
		}
	}
	for(int i=1;i<=n;++i)
	  if(hei[i]==false) {cout<<"impossible";return 0;}
	for(int i=1;i<=top;++i)
	  add(0,p[i],0),add(p[i],0,0);
	spfa();
	sort(edge+1,edge+1+cnt,comp);
	for(int i=1;i<=n;++i) fa[i]=i;
	long long ans=0;
	for(int i=1;i<=cnt;++i)
	{
		if(dis[edge[i].to]==dis[edge[i].u]+edge[i].w)
		{
			int x=edge[i].u,y=edge[i].to;
			if(search(x)!=search(y))
			{
				int fx=search(x);
				int fy=search(y);
				fa[fx]=fy;
				ans+=edge[i].w;
			}
		}
	}
	cout<<ans;
	return 0;
}

T3:gcd

题目描述

给出n个正整数,放入数组 a 里。
问有多少组方案,使得我从 n 个数里取出一个子集,这个子集的 gcd 不为 1 ,然后我再从剩下的数中取出一个数,把他放进刚刚取出的子集里,使得 gcd 为 1 。
输出方案数 mod (109 + 7)。

输入格式

第一行一个数 n 。
第二行 n 个数,表示 a 数组。

输出格式

输出一个数表示答案。

分析:

   考虑容斥原理,利用莫比乌斯函数(orz..)。。。蒟蒻在这里就不深究了,不过30%的数据可以暴力枚举每一个子集再check加的数。就这样吧。


总结:

  这次考试1,2题都是很好的训练题,T1是一道难度不高但思想很好的DP题,T2作为一道图论题,将最短路与最小生成树结合在一起(这两个算法经常一起思考,以(dis[edge[i].to]==dis[x]+edge[i].to)作为桥梁),同时超级点的应用也很值得多思考。T3作为NOIP的模拟测试有一点超了,但是30%的数据暴力分要好好拿到手。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值