您的位置:首页 > 其它

[置顶] 第二季 模拟赛整理

2016-10-19 21:45 225 查看
————————-10.19—————还有一个月—————————–

T1 只拿了55分,还没高一的考的高(****),t1没考虑全面,理解透题意,认真读题。T2字符串输入不会2333333T3运输计划5分滚粗。

Problem 1 a 的商业计划

题目描述

a 不仅程序打得好,还有商业头脑,他发现快递公司很赚钱,于是定也办一个。众所周知,a 办事和他的代码一样,效率很高,所以他很快就找到个 n*n 的地方用来存放快递。为了方便查找,a 希望每个格子上都有快递,并且每行每列每条对线的个数总和相等,当然了,每个格子在允许的情况下放的快递越越好,因为这样可以更快的查找。

现在 a 已经把大部分格子都放上了快递,只有一个格子是空的,他知道在这放多少件快递可以满足他的要求。

输入描述

第一行,包括一个数 n,表示矩形有 n 行 n 列

下面 n 行,每行包括 n 个数,表示矩形内的数。

输出描述

一个正整数,即为满足要求的数。如果无法满足要求,输出-1。

样例输入

3

4 0 2

3 5 7

8 1 6

样例输出

9

数据范围及提示

对于 100%的数据,0 < n <= 500, 0 < 每个格子上的快递数 <=

10^9(不包括空格子处)

题解:就是个模拟没考虑全;发现好长,压了又压100行。

代码:

#include <iostream>
#include <cstdio>
using namespace std;
int mp[600][600];
int he[100000],jl[100];
bool used[100000];
int n,sx,sy;
int main()
{
freopen("business.in","r",stdin);
freopen("business.out","w",stdout);
scanf("%d",&n);
if(n==1) puts("1");
else
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
scanf("%d",&mp[i][j]);
if(mp[i][j]==0) sx=i,sy=j;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
he[i]+=mp[i][j];
for(int i=n+1;i<=n+n;i++)
for(int j=1;j<=n;j++)
he[i]+=mp[j][i-n];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i==j)  he[n+n+1]+=mp[i][j];
if(i+j==n+1)  he[n+n+2]+=mp[i][j];
}
int t=0,tt;
for(int i=1;i<=2*n+2;i++)
{
if(i==sx){
t++;
jl[t]=he[i];
used[i]=1;
}
if(i==sy+n){
t++;
jl[t]=he[i];
used[i]=1;
}
if(sx==sy&&sx+sy!=n+1){
t++;
jl[t]=he[2*n+1];
18997
used[2*n+1]=1;
}
if(sx!=sy&&sx+sy==n+1){
t++;
jl[t]=he[2*n+2];
used[2*n+2]=1;
}
if(sx==sy&&sx+sy==n+1){
t++;
jl[t]=he[2*n+1];
used[2*n+1]=1;
t++;
jl[t]=he[2*n+2];
used[2*n+2]=1;
}
}
int hh=0;
for(int i=1;i<t;i++){
if(jl[i]!=jl[i+1]){
hh=1;
break;
}
}
int ans;
for(int i=1;i<=2*n+2;i++){
if(used[i]==0){
ans=he[i];
break;
}
}
int dq=0;
for(int i=1;i<=2*n+2;i++){
if(dq==0&&used[i]==0){
dq=he[i];
used[i]=1;
}
if(dq!=0&&used[i]==0){
if(dq!=he[i]){
hh=2;
break;
}
}
}
if(hh==1||hh==2)  puts("-1");
else  printf("%d",ans-jl[1]);
}
fclose(stdin);
fclose(stdout);
return 0;
}


T2

Problem 2 幕后黑手

题目描述

众所周知,a 办了一个超大型快递公司,并且 a 是学 oi 的,所以他准备在网上搞一个快递网站方便人们收发快递及查询快递,因为 a 的名气实在太大,所以人们都喜欢到他的公司收发快递,这就引起了竞争对手gbwl 的嫉妒,他们决定建小号偷偷给 a 差评,但是 gbwl 的人实在是又懒又笨,所以他们的小号名字总是很相似,例如“xiaohao1”和“xiaohao2”是相似的,但是“xiaohao1”和“2xiaohao”不是似

的。也就是说,当且仅当两个字符串等长且恰好只有一位不同,才被认为是相似的。现在 a 想知道,在给定的账户名中,有多少对是相似的,但是 a 现在有更重要的工作(比如去黑掉 gbwl 的网站),所以他把这项工作交给了你。为了简化你的工作,a 给你的字符串长度均相等,且只包含大小写字母、数字、下划线以及‘@’共 64 种字符,而且不存在两个相同的账户名称。

输入描述

第一行包含三个正整数 n,m,sz。其中 n 表示账户名称数量,m 表示账

户名称长度,sz 用来表示字符集规模大小,它的值只可能为 2 或 64。

若 sz 等于 2,账户名称中只包含字符‘0’和‘1’共 2 种字符;

若 sz 等于 64,账户名称中可能包含大小写字母、数字、下划线以及

‘@’共 64 种字符。

随后 n 行,每行一个长度为 m 的字符串,用来描述一个账户名称。数据

保证每个字符串是两两不同的。

输出描述

仅一行一个正整数,表示共有多少对相似的账户名称。

样例输入

4 3 64

Fax

fax

max

mac

样例输出

4

数据范围及提示

对于 20%的数据,0

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char str[30000+10][200+10];    //这样存
int main()
{
freopen("gbwl.in","r",stdin);
freopen("gbwl.out","w",stdout);
int n,m,sz;
scanf("%d%d%d",&n,&m,&sz);
for(int i=1;i<=n;i++)
scanf("%s",str[i]); //这样输入
int cnt=0,ans=0;
for(int i=1;i<=n;i++)//暴力枚举
{
for(int j=i+1;j<=n;j++)
{
cnt=0;
for(int k=0;k<m;k++)
{
//cout<<str[i][k]<<" "<<str[j][k]<<endl;
if(str[i][k]!=str[j][k])
cnt++;
if(cnt>1)
break;
}
//cout<<cnt<<endl;
if(cnt==1)
ans++;
}
}
printf("%d\n",ans);
return 0;
}


正解:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ULL;
int n,m,sz;
const int p=2333;
char str[30000+10][550+10];
ULL hash[50000],py[50000],ls[50000];
void init()
{
py[0]=1;
for(int i=1;i<=210;i++)
py[i]=py[i-1]*p;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
hash[i]=hash[i]*p+str[i][j];
}
int main()
{
freopen("gbwl.in","r",stdin);
freopen("gbwl.out","w",stdout);
scanf("%d%d%d",&n,&m,&sz);
for(int i=1;i<=n;i++)
scanf("%s",str[i]+1);
init();
int cnt=1,ans=0;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
ls[j]=hash[j]-str[j][i]*py[m-i];
sort(ls+1,ls+n+1);
for(int k=1;k<n;k++)
{
if(ls[k]==ls[k+1])
{
ans+=cnt;
cnt++;
}
else
cnt=1;
}
}
cout<<ans;
fclose(stdin);
fclose(stdout);
return 0;
}


T3

不会,不是很理解。。。。太弱了。

—— 10.20我们队出题———————–开心————————————-

直接丢题面代码。

Problem 1 小问题

题目描述 mfc被校长开除了。回家养了一些牛,无聊的他在无意中发现了一些比较好玩的事情。举个例子,假如有16头牛,如果建了3个牛圈,剩下1头牛就没有地方安家了。如果建造了5个牛圈,但是仍然有1头牛没有地方去,然后如果建造了7个牛圈,还有2头没有地方去。他想拿这个来考一考qer,看他能不能知道至少有几头牛。但是qer觉得这个问题太简单啦根本不想因为这个浪费时间,但是如果qer不回答会削弱他们之间的情谊♂。于是他就找到了你,让你回答这个无聊的问题。

输入描述 第一行包含一个整数n (n <= 10) – 建立牛圈的次数,解下来n行,每行两个整数ai, bi( bi <= ai <= 1000), 表示建立了ai个牛圈,有bi头牛没有去处。

你可以假定ai,aj互质.

输出描述 输出包含一个正整数,即为mfc至少养的牛的数目。

样例输入 3

3 1

5 1

7 2

样例输出 16

数据范围及提示 送分题随意乱搞233

题解:中国剩余定理 + exgcd 233

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN=1000+10;
int a[MAXN],b[MAXN];

LL exgcd(LL a,LL b,LL &x,LL &y)//扩展欧几里得
{
if(b==0)
{
x=1;
y=0;
return a;
}
else
{
LL p=exgcd(b,a%b,x,y);
LL t=x;
x=y;
y=t-a/b*x;
return p;
}

}

/*x%a[i]==b[i]*/
LL China(int n)//中国剩余定理
{
LL M=1,d,x=0,y;
for(int i=1;i<=n;i++)
M*=(LL)a[i];
for(int i=1;i<=n;i++)
{
LL w=M/(LL)a[i];
exgcd(w,a[i],d,y);
x=(x+d*w*b[i])%M;
}
return (x+M)%M;
}

int main()
{
//freopen("Question.in","r",stdin);
//freopen("Question.ans","w",stdout);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i],&b[i]);
printf("%lld\n",China(n));
return 0;
}


T2

Problem 2 挖掘机

题目背景 一中机房谁最虚?高二一级部5班mfc!感觉很顺,是吧?mfc养牛赔了很多钱。于是去了新东方学一些技术(你猜是学英语的还是学挖掘机的)。后来学校看他可怜又让他去上学啦。

题目描述 今天,mfc开着挖掘机去上学。但是他发现上学的mz满街都是,所以一路上他碰到了好多mz。一开始他以1km/min的速度(=60km/h……)开着挖掘机前进。他发现他只会在恰好到达某一时刻或者到达某个距离遇到mz。每次遇到mz,mfc都会毫不犹豫的把她们顺路捎走(^_^)。但是他实在是太虚了,以至于当有i个mz时他的速度下降到1/(i+1)。具体说,一开始mfc以1km/min速度前进,有1个mz的时候速度变为1/2 km/min,有2个时变为1/3 km/min……以此类推。给出每个mz在何时出现,现在问题来了,mz们关心会不会迟到所以问mfc到学校要多久。(mfc说这么简单的问题我肯定能AC啊,说完就虚了悄悄的给正在机房考试的qer打了个电话,然后qer找到了你,让你帮助mfc来解决问题。)

输入描述 输入第一行2个数n,m,分别表示mz的个数和mfc与学校的距离(km)

接下来2到n+1行由字符串与数字构成

Dist x表示在距离达到x km时出现一个mz

Time x表示在时间达到x min时出现一个mz

输出描述 一个整数,表示到达学校的时间。(如果不能整除,直接输出整数部分即可)。

样例输入 2 20

Time 3

Dist 10

样例输出 47

数据范围及提示 对于30%数据,n,m<=50

对于50%数据,n,m<=2000

对于100%数据,n,m<=200000,x<=10^9,保证输入的数字都是整数

题解:

枚举,注意处理细节,边界问题。

#include<cstdio>
#include<cmath>
#include<algorithm>
#define inf 598460606
#define LL long long
using namespace std;
int n,m,per=1;
int lt,ld,nt=1;
char ch[5];
double t[210000],d[210000],x,nowt;
int main()
{
//freopen("dig.in","r",stdin);
//freopen("dig.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%s%lf",ch,&x);
if (ch[0]=='T')t[++lt]=x;
else d[++ld]=x;
}
d[++ld]=0.0;  //从零开始
d[++ld]=m;    //处理边界
sort(d+1,d+ld+1);
sort(t+1,t+lt+1);
for(int i=1;i<ld;i++)
{
double nd=d[i];
while (nd<d[i+1]&&nt<=lt&&nowt+(d[i+1]-nd)*per>t[nt])
{
nd+=(t[nt]-nowt)/per;
per++;
nowt=t[nt++];
}
nowt+=(d[i+1]-nd)*per;
per++;
}
printf("%I64d\n",(long long)nowt);
}


T3

Problem 3 八心八箭南非真钻表面镀金守卫结界

题目描述 Qer发现mfc打电话居然是为了讨好妹子!顿时怒由心生!运起十成功力,誓要取mfc项上人头!

mfc见qer要杀他:“小样!先过了我的八心八箭南非真钻表面镀金守卫结界吧23333”

于是……qer把你逮住,让你把这个守卫结界给破解掉。

这是一个有N个节点,M条边的带权有向图.qer要求你写一个程序, 判断这个有向图中是否存在负权回路. 如果从一个点沿着某条路径出发, 又回到了自己, 而且所经过的边上的权和小于0, 就说这条路是一个负权回路.如果存在负权回路, 只输出一行-1;如果不存在负权回路, 再求出一个点S(1 <= S <= N)到每个点的最短路的长度. 约定: S到S的距离为0, 如果S与这个点不连通, 则输出NoPath.

输入描述 第一行: 点数N, 边数M, 源点S;

以下M行, 每行三个整数a, b, c表示点a, b之间连有一条边, 权值为c

输出描述 如果存在负权环, 只输出一行-1, 否则按以下格式输出

共N行, 第i行描述S点到点i的最短路:

如果S与i不连通, 输出NoPath;

如果i = S, 输出0;

其他情况输出S到i的最短路的长度.

样例输入 6 8 1

1 3 4

1 2 6

3 4 -7

6 4 2

2 4 5

3 6 3

4 5 1

3 5 4

样例输出 0

6

4

-3

-2

7

数据范围及提示 对于40%数据,2<=N<=50,1<=M<=N^2-1(其实并没有什么卵用233)

对于100%数据:2 <= N <= 1,000,1<=M <= 100,000,1 <= S <= N,1 <= a, b <= N,-1,000,000 <= c <= 1,000,000

注:puts(“-1”)手动CE

题解 :

没看题 233,先附上代码吧。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
using namespace std;
int head[101000],tot;
long long dist[101000];
int fa[101000];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int n,m,S;
struct Edge
{
int ff,tt,dd,next;
}edge[1000100];
inline void build(int ff,int tt,int dd)
{
edge[++tot].ff=ff;
edge[tot].tt=tt;
edge[tot].dd=dd;
edge[tot].next=head[ff];
head[ff]=tot;
}
bool passby[101000];
int passed[101000];
deque<int>q;
bool flag;
void spfa(int s)
{
passby[s]=1;
dist[s]=0;
passed[s]=1;
q.push_back(s);
while(!q.empty())
{
int x=q.front();
q.pop_front();
passby[x]=0;
for(int i=head[x];i;i=edge[i].next)
{
Edge e=edge[i];
if(find(e.tt)!=find(s))
{
fa[find(e.tt)]=find(s);
}
if(dist[e.tt]>dist[x]+e.dd)
{
dist[e.tt]=dist[x]+e.dd;
if(!passby[e.tt])
{
passed[e.tt]++;
if(passed[e.tt]>n)
{
cout<<"-1";
flag=1;
return;
}
if(dist[e.tt]<=q.front())
{
q.push_front(e.tt);
passby[e.tt]=1;
}
else
{
q.push_back(e.tt);
passby[e.tt]=1;
}
}
}
}
}
}
int main()
{
freopen("EasySSSP.in","r",stdin);
freopen("EasySSSP.out","w",stdout);
cin>>n>>m>>S;
for(int i=0;i<=n+5;i++)
{
fa[i]=i;
dist[i]=0x7fffffff;
}
for(int i=0;i<m;i++)
{
int ff,tt,dd;
cin>>ff>>tt>>dd;
build(ff,tt,dd);
}
spfa(S);if(flag)return 0;
for(int i=1;i<=n;i++)
{
if(passed[i]==0)spfa(i);
if(flag)return 0;
}
for(int i=1;i<=n;i++)
{
if(find(S)!=find(i))
{
cout<<"NoPath"<<endl;
continue;
}
if(i==S)
{
cout<<'0'<<endl;
continue;
}
cout<<dist[i]<<endl;
}
return 0;
}


T4

Problem 4 mfc学数学

题目描述 月考后mfc悲催的发现在家养了几天牛导致数学挂科了,他去找数学老师给自己辅导功课,数学老师为了锻(chao)炼(feng)他的反(zhi)应(shang)速(gan)度(ren),给他出了一道难题。题目包含两种操作:

1.在当前集合中加入一个数X;

2.查询Y。即询问当前集合中 所有元素 mod Y后的最小值是多少。

只进行了几次操作,mfc就感觉支撑不住了,于是他找到了你,请你帮忙解决这一问题。

输入描述 第一行一个整数n,是接下来将要操作的次数。 接下来n行,每行第一个字符为’A’或’B’,接下来是一个整数X或Y。

若字符为’A’,则执行操作1,插入X;

若字符为’B’,则执行操作2,查询Y(Y>0);询问所有元素modY可以产生的最小值是多少。

保证第一个操作为操作1。

输出描述 对于每一个操作2,输出一行一个整数,表示 元素mod Y后,最小值可以是多少。

样例输入 6

A 8

B 5

A 6

B 5

A 11

B 3

样例输出 3

1

0

数据范围及提示 测试点编号 N的范围 X的范围 Y的范围

1,2 <=1000 <=50000 <=500

3,4 <=50000 <=50000 <=500

5,6,7 <=50000 <=50000 <=50000

8,9,10 <=100000 <=200000 <=200000

提示:数据非常水,请随意乱搞

样例解释 在第一次询问时,集合中只有一个元素,8%5=3,所以最小值为3;

在第二次询问时,集合中为6,8。6%5=1,8%5=3,所以最小值为1;在第三次询问时,集合为8,6,11。6%3=0,8%3=2,11%3=2;所以最小值为0;

题解:lc说是并查集 - -

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int f[300005],num[300005];
int mn[605],ans[300005];
int n;
char opt[100005];
int S[1000005],top;
int find(int i)
{
while(f[i]!=i)
{
S[++top]=i;
i=f[i];
}
while(top)
{
f[S[top]]=i;
top--;
}
return i;
}
int cnt[300005];
char s[5];
int main()
{
freopen("Math.in","r",stdin);
freopen("Math.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=300000;i++) f[i]=i+1;
memset(mn,127,sizeof(mn));
for (int i=1;i<=n;i++)
{
scanf("%s",s);
opt[i]=s[0];
scanf("%d",&num[i]);
if (opt[i]=='A')
{
for (int j=1;j<=540;j++) mn[j]=min(mn[j],num[i]%j);
f[num[i]]=num[i];cnt[num[i]]++;
}
else if (num[i]<=540) ans[i]=mn[num[i]];
}
for (int i=n;i;i--)
{
if (opt[i]=='A')
{
cnt[num[i]]--;
if(cnt[num[i]]==0)
f[num[i]]=f[num[i]+1];
}
else if(num[i]>540)
{
ans[i]=find(1)%num[i];
for (int j=num[i];j<=300000;j+=num[i])
{
int x=find(j);
if (x) ans[i]=min(ans[i],x%num[i]);
}
}
}
for (int i=1;i<=n;i++)
if (opt[i]=='B') printf("%d\n",ans[i]);
return 0;
}


————————————10.21———————————————-

400 - - 90 ,t1据说是某次cf的B题,由于考虑的情况少了两种so只有40分,愁人- -

t2状压dpcodves原题解药还是毒药,不会,数据良心给了40分。T3 k短路需要迪杰斯特拉,不会只会spfa10分。T4树形dp头回听说233我太弱了,寻思打个贪心,想了两种貌似打出来就是正解的贪心,可是对我来说太难打了1个多小时没了。以后做题要快点做,先做简单的有思路的。思路不清晰的最后再做,最后15分钟检查一下程序别犯sx错误,做简单题时一定要考虑小数据,极限数据。自己想全不要像今天一样明明能拿100却只拿了40。

Problem 1 Chlorine 和奇怪的信

题目描述 某一天,Chlorine 收到了一封英语信,但是他是个英语考不上 140 的学渣(连 G 和 J 都分不清怎么读),根本看不懂这封信,他只好选择一个单词一个单词地对着百度翻译找…找了好一段时间彻底弃疗,然后他对着这封信玩了起来…

输入描述 输入的第一行是一个正整数 n,为这封信的长度。输入的第二行是一个字符串(字符串里只有下划线,左右括号和字母,括号内没有括号,比如

“(())”这种情况是不存在的),长度为 n

输出描述

由于 Chlorine 实在是太无聊了。所以他在信上加了很多括号和下划线。输出的第一个数字为非负整数,为括号外的最长的单词的长度(下划线不属于单词,比如”ha_ha”这个字符串,包括两个单词,最长的单词长度为 2) 输出的第二个数字为非负整数(两个数字用一个空格隔开)表示括号内的单词的总数(比如”(ha_ha)”这个字符串,包括两个单词)

样例输入 40

Ich_lie(be_di_ch_Ch)_(lo_rine)_H_ikari

样例输出 5 6

数据范围及

提示

对于 100%的数据

1<=n<=255

题解:

暴力模拟,依次比较,特殊情况要想全。

#include <iostream>
#include <cstdio>
using namespace std;
char s[300];
int main()
{
freopen("letter.in","r",stdin);
freopen("letter.osu","w",stdout);
int n;
scanf("%d",&n);
scanf("%s",s+1);
int q=0,h=0,tot=0,ans=0,ans2=0;
for(int i=1;i<=n;i++)
{
if(q==1)
{
if(s[i]!=')')
{
if(s[i]!='_'&&h==0)
{
h=1;
ans2++;
}
if(s[i]=='_')
h=0;
continue;    //操作完了直接跳到下一层
}
if(s[i]==')')
{
q=0;
h=0;
continue;
}
}
if(s[i]=='_')
{
ans=max(tot,ans);
tot=0;
continue;
}
if(s[i]=='(')
{
ans=max(tot,ans);       //遇到括号也算结束所有得去一下最大值a(a)aa
tot=0;
q=1;
continue;
}
if(s[i]!='_'&&s[i]!='('&&s[i]!=')')
tot++;
if(i==n)
ans=max(tot,ans);
}
printf("%d %d",ans,ans2);
fclose(stdin);
fclose(stdout);
return 0;
}


Problem 2 Yuan 和膜法阵

题目描述 Yuan 旅游到了一个村庄,在村里住过几天后,他发现好像有点不对劲,因为这个村庄里的人经常排队去暴力膜蛤,所以几天前这个村子被圣膜法师乡冈季浙用 n 个膜法结界给封住了,导致村子里的人都出不去,并且乡冈季浙还修改了村子里的膜法场强,这个村子里有 m 个膜法师,每个膜法师能力都不同,(比如说有 5 个膜法结界,第三个和第四个膜法结界已经被打破,膜法师 ai 能打破第一个和第二个膜法结界,但是由于村子内膜法场强的混乱,反而会修复第三个和第四个膜法结界。)Yuan 被村里人要求去请魔法师来破坏膜法结界,如果不能破坏所有膜法结界,村子里的人将永远出不去,每一个膜法师可以被雇佣多次,花 1RMB就可以雇佣某个膜法师一次,Yuan 不想花太多的RMB,所以你需要帮助 Yuan 来花尽量少的 RMB来破坏结界。

输入描述

输入的第一行为 n,表示有 n 个膜法结界。

输入的第二行为 m,表示有 m 个膜法师。

接下来 m 行,每行有 n 个数(只能是 0,1 和-1),

表示该膜法师的能力。

(如果当前 n 为 5,且魔法师 ai 能力值为 1 1 -1 -1

0,表示膜法师 ai 能打破第一个和第二个膜法结

界,但是由于膜法场强的混乱,反而会生成第三

个和第四个膜法结界,并且他不会对结界 5 造成

任何影响)

输出描述

如果能打开结界,就输出最少花多少 RMB 才能打

开结界。

如果不能打开,输出”GG”(不包括引号)。

样例输入 3

2

1 0 1

-1 1 0

样例输出 2

数据范围及

提示

对于 10%的数据,m=1

对于另外 20%的数据,m=2

对于 100%的数据

1<=n<=10

0<=m<=100

题解:我把sys拉来给我讲的Orz,据说这叫状压bfs,在第一季的那个迷宫貌似就是状压bfs,然而我并没有发现- - (是时候得回过头去整理整理了),用二进制表示状态,利用位运算维护状态。大概就这样了。

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
struct dqs
{
int c,z;
};
int mf[110][15],n,m;
bool used[20000];
queue <dqs> q;
int bfs()
{
q.push((dqs){0,(1<<n)-1});
while (!q.empty())
{
dqs x=q.front();
q.pop();
used[x.z]=1;
for(int i=0;i<m;i++)
{
int z=x.z;
for(int j=0;j<n;j++)
{
if(mf[i][j]==1)
z&=(1<<j)^((1<<n)-1);
else if(mf[i][j]==-1)
z|=(1<<j);
}
if(z==0)
return x.c+1;
if(!used[z])
q.push((dqs){x.c+1,z});
}
}
return -1;
}

int main()
{
//freopen("glgjssy.In","r",stdin);
//freopen("glgjssy.0ut","w",stdout);
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
scanf("%d",&mf[i][j]);
int ans=bfs();
if(ans>0) printf("%d",ans);
else puts("GG");
fclose(stdin);
fclose(stdout);
return 0;
}


T3

原题k短路 , 不会,果断改题面。

codves 1269 匈牙利游戏 时间限制: 1 s 空间限制: 128000 KB

题目描述 Description

欢迎来到匈牙利游戏!布达佩斯(匈牙利首都)的街道形成了一个弯曲的单向网络。

你被强制要求参加一个赛跑作为一个TV秀的一部分节目,比赛中你需要穿越这些街道,从s开始,到t结束。很自然的,你想要尽快的完成比赛,因为你的比赛完成的越好,你就能得到更多的商业促销合同。但是,有一个需要了解的是,如果有人过于聪明找到从s到t的最短路线,那么他就被扔到国家极品人类保护系统中作为一个国家宝藏收藏起来。你显然要避免这种事情的发生,但是也想越快越好。写一个程序来计算一个从s到t的严格次短路线吧。有的时候,严格次短路线可能访问某些节点不止一次。样例2是一个例子。

输入描述

第一行包含两个整数N和M,N代表布达佩斯的节点个数,M代表边的个数。节点编号从1到N。1代表出发点s,N代表终点t。接下来的M行每行三个整数A B L,代表有一条从A到B的长度为L的单向同路。你可以认为A不等于B,也不会有重复的(A,B)对。

输出描述

输出从s到t的严格次短路的长度。如果从s到t的路少于2条,输出-1。

样例输入 Sample Input

样例输入1:

4 6

1 2 5

1 3 5

2 3 1

2 4 5

3 4 5

1 4 13

样例输入2:

2 2

1 2 1

2 1 1

样例输出 Sample Output

样例输出1:

11

样例输出2:

3

数据范围及提示 Data Size & Hint

对于样例1:

There are two shortest routes of length 10 (1 → 2 → 4,1 → 3 → 4) and the strictly-second- shortest route is 1 → 2 → 3 → 4 with length 11.

对于样例2:

The shortest route is 1 → 2 of length 1, and the strictly-second route is 1 → 2 → 1 → 2 of length 3.

原来是英文题面,果断改中文。

题解:求严格次短路,spfa稍加处理即可。判断顺序,先最短更新次短,不能更新最短但能更新次短,最后次短更新次短。

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
int V,E,s,t;
struct edge
{
int f,t,d;
}es[300000];
bool used[300000];
int tot=0,first[300000],next[300000],dis[300000],dis2[300000];
void build(int f,int t,int d)
{
es[++tot]=(edge){f,t,d};
next[tot]=first[f];
first[f]=tot;
}
queue <int> q;
void spfa (int x)
{
dis[x]=0;

q.push(x);
used[x]=1;
while (!q.empty())
{
int u=q.front();
q.pop();
used[u]=0;
for(int i=first[u];i!=0;i=next[i])
{
int v=es[i].t;
if(dis[v]>dis[u]+es[i].d)
{
dis2[v]=dis[v];
dis[v]=dis[u]+es[i].d;
if(used[v]==0)
{
q.push(v);
used[v]=1;
}
}
else if(dis[v]<dis[u]+es[i].d&&dis2[v]>dis[u]+es[i].d)  //貌似这个必须得放在下一个判断的上面   * - *
{
dis2[v]=dis[u]+es[i].d;
if(used[v]==0)
{
q.push(v);
used[v]=1;
}
}
else if(dis2[v]>dis2[u]+es[i].d)
{
dis2[v]=dis2[u]+es[i].d;
if(used[v]==0)
{
q.push(v);
used[v]=1;
}
}
}
}
}
int inf=1e9+7;
int main()
{
scanf("%d%d",&V,&E);
for(int i=1;i<=V;i++)
{
dis[i]=inf;
dis2[i]=inf;
}
int xx,yy,zz;
for(int i=1;i<=E;i++)
{
scanf("%d%d%d",&xx,&yy,&zz);
build(xx,yy,zz);
}
spfa(1);
if(dis2[V]==inf)
puts("-1");
else
printf("%d",dis2[V]);
return 0;
}


T4

codves 1378 选课 时间限制: 1 s 空间限制: 128000 KB

题目描述 Description

学校实行学分制。每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。学校开设了N(N<300)门的选修课程,每个学生可选课程的数量M是给定的。学生选修了这M门课并考核通过就能获得相应的学分。在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。例如《Frontpage》必须在选修了《Windows操作基础》之后才能选修。我们称《Windows操作基础》是《Frontpage》的先修课。每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。每门课都有一个课号,依次为1,2,3,…。 例如:

【详见图片】

表中1是2的先修课,2是3、4的先修课。如果要选3,那么1和2都一定已被选修过。   你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修课优先的原则。假定课程之间不存在时间上的冲突。

输入描述 Input Description

输入文件的第一行包括两个整数N、M(中间用一个空格隔开)其中1≤N≤300,1≤M≤N。

以下N行每行代表一门课。课号依次为1,2,…,N。每行有两个数(用一个空格隔开),第一个数为这门课先修课的课号(若不存在先修课则该项为0),第二个数为这门课的学分。学分是不超过10的正整数。

输出描述 Output Description

输出文件只有一个数,实际所选课程的学分总数。

样例输入 Sample Input

7 4

2 2

0 1

0 4

2 1

7 1

7 6

2 2

样例输出 Sample Output

13

数据范围及提示 Data Size & Hint

各个测试点1s

题解:

树形dp,记忆化搜索。

代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int SZ= 5010;
int n,m;
int ch[SZ][5],w[SZ],dp[SZ][SZ];
int dfs(int u,int v)
{
if(u==0) //如果他没有儿子了
return 0;
if(dp[u][v]!=0)
return dp[u][v];
for(int i=0;i<=v-1;i++)
dp[u][v]=max(dp[u][v],w[u]+dfs(ch[u][0],i)+dfs(ch[u][1],v-i-1));
dp[u][v]=max(dp[u][v],dfs(ch[u][1],v));
return dp[u][v];
}
int main()
{
freopen("dreamerpeacefuldoge.in","r",stdin);
freopen("dreamerpeacefuldoge.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int fa;
scanf("%d%d",&fa,&w[i]);
if(ch[fa][0]!=0)    // 左儿子右兄弟   把一个多叉树变成二叉树
ch[i][1]=ch[fa][0];
ch[fa][0]=i;
}
printf("%d",dfs(ch[0][0],m));
fclose(stdin);
fclose(stdout);
return 0;
}


整理算是整理完了,得理解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: