【NOIP模拟】 (7.17)

24 篇文章 0 订阅
20 篇文章 0 订阅

T1

题目描述

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

输入格式

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

输出格式

一行一个整数表示答案。

样例数据 1

输入

1800777700088888000777

输出

13

备注

【数据范围】
对 30% 的输入数据 :字符串长度≤100 ;
对 100% 的输入数据 :字符串长度≤1000000 


解析:

       对于30%的数据,枚举最后一个1,最后一个0,最后一个8的位置,分别搜索第一段,第二段,第三段和最后一段各有几个1、8、0、7,用和来更新最大值。复杂度O(n^4)

       对于100%的数据,动态规划,f[i][j]表示前i位,当前用到了第j个数字(分别指1、8、0、7),最后f[n][4]即为答案。复杂度O(n)


代码如下:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <cctype>
#include <ctime>
#include <queue>
#include <vector>
#include <sstream>
using namespace std;

int n;
int f[1000010][5];
char num[1000010];

int get_int()
{
   int x=0,f=1;
   char c;
   for(c=getchar();(c<'0'||c>'9')&&(c!='-');c=getchar());
   if(c=='-') {f=-1; c=getchar();}
   for(;c>='0'&&c<='9';c=getchar()) x=(x<<3)+(x<<1)+c-'0';
   return x*f;
}

int main()
{
  // freopen("1807.in","r",stdin);
  // freopen("1807.out","w",stdout);
   memset(f,128,sizeof(f));

   scanf("%s",num+1);
   n=strlen(num+1);
   f[0][0]=0;
   for(int i=1;i<=n;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(num[i]=='1') f[i][1]=max(f[i][1],f[i-1][0])+1;
	 if(num[i]=='8') f[i][2]=max(f[i][2],f[i-1][1])+1;
	 if(num[i]=='0') f[i][3]=max(f[i][3],f[i-1][2])+1;
	 if(num[i]=='7') f[i][4]=max(f[i][4],f[i-1][3])+1;
   }

   cout<<max(f[n][4],0)<<endl;
   return 0;
}

T2

题目描述

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

输入格式

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

输出格式

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

样例数据 1

输入

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 。


解析:

      对于30%的数据,枚举每条边的选取情况,暴力判断。

      对于100%的数据,添加一个超级点,使得它与每个黑点相连且权值为0,用SPFA处理从S出发的最短路,问题就变成了选取权值最小的边的集合,使这幅图连通,于是就可以用最小生成树的Kruskal。


代码如下:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <cctype>
#include <ctime>
#include <queue>
#include <vector>
#include <sstream>
using namespace std;

const int Max=1000100;

long long n,m,ans;

struct spfa{int to,next,len;};
spfa bian[Max];
long long s1,head,tail;
long long first[Max],p[Max],dis[Max];
bool exist[Max];

struct kruskal{int x,y,z;};
kruskal size[Max];
int father[Max];
long long s2;

int get_int()
{
   int x=0,f=1;
   char c;
   for(c=getchar();(c<'0'||c>'9')&&(c!='-');c=getchar());
   if(c=='-') {f=-1; c=getchar();}
   for(;c>='0'&&c<='9';c=getchar()) x=(x<<3)+(x<<1)+c-'0';
   return x*f;
}

void clear()
{
   s1=0;s2=0;
   memset(bian,0,sizeof(bian));
   memset(first,0,sizeof(first));
   memset(p,0,sizeof(p));
   memset(dis,127,sizeof(dis));
   memset(father,0,sizeof(father));
}

int getfather(long long v)
{
   if(father[v]==v) return v;
   father[v]=getfather(father[v]);
   return father[v];
}

void merge(long long x,long long y)
{
   int i=getfather(x);
   int j=getfather(y);
   if(i!=j) father[i]=j;
}

void tree()
{
   for(long long i=1;i<=n;i++) father[i]=i;
   for(long long i=1;i<=s2;i++)
   {
   	 if(getfather(size[i].x)!=getfather(size[i].y))
   	 {
   	   ans+=size[i].z;
   	   merge(size[i].x,size[i].y);
   	 }
   }
}

bool comp(const kruskal &a,const kruskal &b)
{
   return a.z<b.z;
}

void search(int x)
{
   for(long long u=first[x];u;u=bian[u].next)
   	 if(dis[x]+bian[u].len==dis[bian[u].to])
   	 {
   	   search(bian[u].to);
   	   size[++s2].x=x;
   	   size[s2].y=bian[u].to;
   	   size[s2].z=bian[u].len;
   	 }
}

void SPFA()
{
   memset(exist,false,sizeof(exist));
   p[1]=0;
   dis[0]=0;
   exist[0]=true;
   head=0;
   tail=1;
   while(head<tail)
   {
   	 head++;
   	 exist[p[head]]=false;
   	 long long u=first[p[head]];
   	 while(u!=0)
   	 {
   	   if(dis[p[head]]+bian[u].len<dis[bian[u].to])
   	   {
   	   	 dis[bian[u].to]=dis[p[head]]+bian[u].len;
   	   	 if(exist[bian[u].to]==false)
   	   	 {
   	   	   tail++;
   	   	   p[tail]=bian[u].to;
   	   	   exist[bian[u].to]=true;
   	   	 }
   	   }
   	   u=bian[u].next;
   	 }
   }
}

void build(long long x,long long y,long long z)
{
   s1++;
   bian[s1].next=first[x];
   first[x]=s1;
   bian[s1].to=y;
   bian[s1].len=z;
}

int main()
{
   //freopen("minimum.in","r",stdin);
   //freopen("minimum.out","w",stdout);

   while(scanf("%d%d\n",&n,&m)!=EOF)
   {
   	 clear();

   	 for(long long i=1;i<=n;i++)
   	 {
   	   int x;
   	   x=get_int();
   	   if(x) build(0,i,0);
   	 }

   	 for(long long i=1;i<=m;i++)
   	 {
   	   int x,y,z;
   	   x=get_int();
   	   y=get_int();
   	   z=get_int();
   	   build(x,y,z);
   	   build(y,x,z);
     }

     SPFA();
     search(0);
     sort(size+1,size+s2+1,comp);
     tree();

     if(ans==0) cout<<"impossible"<<endl;
     else cout<<ans<<endl;
   }

   return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值