Porble 1时间与空间之旅(tstrip.*)
题目描述
公元22××年,宇宙中最普遍的交通工具是spaceship。spaceship的出现使得星系之间的联系变得更为紧密,所以spaceship船长也成了最热门的职业之一。当然,要成为一名出色的船长,必须通过严格的考核,例如下面是最简单的问题中的一个。
用1~n的整数给n个星系标号,目前你在标号为1的星系,你需要送快递到标号为n的星系,星系之间由于存在陨石带,并不是都可以直连的。同时,由于超时空隧道的存在,在某些星系间飞行会出现时间静止甚至倒流,飞行时间为0或为负数。另外,由星系i到星系j的时间和由星系j到星系i的时间不一定是相同的。
在寄出日期之前收到快递被认为是不允许的,所以每部spaceship上都有一个速度调节装置,可以调节飞行的时间。简单来说其功能就是让所有两个星系间的飞行时间(如果可以直达)都增加或减少相同的整数值,你的任务就是调整速度调节器,找出一条用最短时间完成任务的路径,并且保证这个最短时间的值大于或等于0。
输入格式
输入文件包含多组数据,第1个数为T,表示数据的数量。
对于每一组数据,输入第1行为两个正整数N(2≤N≤100),E(1≤E≤N*(N-1)/2),为星系的个数和星系间飞行的路线数。然后E行,每行三个整数i,j和t(1≤i,j≤N,i≠j,-100000≤t≤100000),表示由星系i到星系j飞行的时间为t。由i到j最多只会有一条飞行线路。
输出格式
输出文件共T行,每组数据输出一行;
如果可以通过调节速度调节器完成任务,则输出一个非负整数,表示由星系1到星系N的最短时间。
如果不能由星系1到达星系N,则输出-1。
输入样例
1
4 5
1 2 1
1 3 1
2 3 -3
3 1 1
3 4 1
输出样例
2
样例说明
输入样例如图所示,其中节点标号表示相应星系,节点间数字表示所需时间。
如果设置速度控制器的值为0,则有如下路径:1→2→3→1→2→……→3→4,使得投递的时间为负无穷大,显然是不符合要求的,所以应该把速度控制器的值设为1,相当于每个时间值加1,得到的最短路径为1→2→3→4,所需时间为2+(-2)+2=2。
/* 唯一一个会做的题还被卡了 多组数据啊.... 考虑的不全面 没造数据 也没拍..... 有种情况就是 如果负环不在1-n的最短路上 要把它忽略. 先把到不了n的删掉再来二分. */ #include<iostream> #include<cstdio> #include<cstring> #include<queue> #define maxn 10010 #define inf 100000 using namespace std; int T,n,m,num,head[110],c[110],dis[110],f[110],ans; int Head[110],Num,Ca[110]; queue<int>q; struct node{ int v,t,pre; }e[maxn],E[maxn]; int init(){ int x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } void Clear(){ num=Num=0;ans=-inf-1; memset(Ca,0,sizeof(Ca)); memset(head,0,sizeof(head)); memset(Head,0,sizeof(Head)); } void add(int from,int to,int dis){ num++;e[num].v=to; e[num].t=dis; e[num].pre=head[from]; head[from]=num; } void Add(int from,int to,int dis){ Num++;E[Num].v=to; E[Num].t=dis; E[Num].pre=Head[from]; Head[from]=Num; } void Dfs(int x){ Ca[x]=1; for(int i=Head[x];i;i=E[i].pre) if(Ca[E[i].v]==0)Dfs(E[i].v); } bool Judge(int x){ while(!q.empty())q.pop(); memset(f,0,sizeof(f)); memset(c,0,sizeof(c)); memset(dis,127/3,sizeof(dis)); dis[1]=0;f[1]=1; q.push(1);c[1]++; while(!q.empty()){ int k=q.front(); q.pop();f[k]=0; if(c[k]>n)return 0; for(int i=head[k];i;i=e[i].pre){ int v=e[i].v; if(Ca[v]==0)continue; if(dis[v]>dis[k]+e[i].t+x){ dis[v]=dis[k]+e[i].t+x; if(f[v]==0){ c[v]++;f[v]=1;q.push(v); } } } } if(dis[n]<0||dis[n]==dis[0])return 0; else return 1; } int main() { freopen("tstrip.in","r",stdin); freopen("tstrip.out","w",stdout); T=init(); while(T--){ n=init();m=init(); Clear();int u,v,t; for(int i=1;i<=m;i++){ u=init();v=init();t=init(); add(u,v,t);Add(v,u,t); } Dfs(n); int l=-inf,r=inf; while(l<=r){ int mid=(l+r)/2; if(Judge(mid)){ r=mid-1;ans=(ans,dis[n]); } else l=mid+1; } if(ans==-inf-1)printf("-1 "); else printf("%d ",ans); } }
Problem 2 狐狸的谜语(puzzle.*)
题目描述
话说某一个月黑风高的晚上,一只褐色的狐狸快速地跳过了一只懒狗,并留下一个字符串“032089”和一个数字5。
这其中一定隐含了某些秘密!酷爱思考的你马上发现,这个字符串可以写成:“03+2+0*89”,结果为5。这是一个非常有趣的问题!
现在给出一个长度为N的数字字符串和一个数字T,要求插入最少的加号或者乘号,使得数字字符串的运算结果为T。运算符*号优先级高于+号,运算数可以有任意个前导0。
榆入格式
输入不超过5组数据,每组数据两行。
每组数据的第1行为长度为N,只包含0~9的数字字符串,第2行为一个数字T。
输入T<0表示输入结束。
输出格式
输出一个数字单独占一行,表示最少需要添加的运算符(+号或*号)数,无解输出-1。
输入样例
032089
5
333
9
00
-1
输出样例
3
2
数据范围
对于30%的数据,有1≤N≤10,0≤T≤50。
对于50%的数据,有1≤N≤15,0≤T≤200。
对于全部的数据,有1≤N≤20,0≤T≤200。
/* 普通的迭代 30分 又是多组数据... 枚举加sum个符号 暴力搞出放在哪 最后 算值 还写了个表达式求值 显然有点浪费.. */ #include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; int T,c[30],ans,falg,len,sum; char s[30]; bool Judge(){ int l=0,s1=0,s2=0,p=0; ll x,y,A[40]={0}; char a[40]={0},B[40]={0}; for(int i=0;i<len;i++){ a[l++]=s[i]; if(c[i]==1)a[l++]='+'; if(c[i]==2)a[l++]='*'; } while(p<l){ if(a[p]<'0'||a[p]>'9'){ if(a[p]=='*')B[++s2]='*'; else{ while(s2&&s1>=2){ x=A[s1];y=A[s1-1]; if(B[s2]=='+')x+=y; else x*=y; s2--;s1--;A[s1]=x; } B[++s2]='+'; } p++; } else{ x=0; while(a[p]>='0'&&a[p]<='9'){ x=x*10+a[p]-'0';p++; } A[++s1]=x; } } while(s2&&s1>=2){ x=A[s1];y=A[s1-1]; if(B[s2]=='+')x+=y; else x*=y; s2--;s1--;A[s1]=x; } return A[1]==T; } void Dfs(int now,int num){ if(now==len-1||num==sum){ if(Judge())falg=1; return; } c[now]=1;Dfs(now+1,num+1); c[now]=0;if(falg)return; c[now]=2;Dfs(now+1,num+1); c[now]=0;if(falg)return; Dfs(now+1,num);if(falg)return; } int main() { freopen("puzzle.in","r",stdin); freopen("puzzle.out","w",stdout); while(1){ ans=20;falg=0; scanf("%s%d",s,&T); if(T<0)break; len=strlen(s); for(int i=0;i<len;i++){ sum=i;Dfs(0,0); if(falg){ ans=i;break; } } if(ans==20)printf("-1 "); else printf("%d ",ans); } return 0; }
/* 正解不是放到最后一块算 而是边添边算 至于优先级问题 维护最后几个数的乘积和这之前的值就好了 这样能剪枝了! 因为给定T是有范围的 +*只会使值变大(除了后面*0) 然后大了就剪掉 注意处理一下保证后面没有零 这个写迭代居然T一个点 改成二分就好了 */ #include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; int T,c[30],ans,falg,len,sum,r[30][30],a[30]; char s[30]; void Solve(){ for(int i=1;i<=len;i++) for(int j=i;j<=len;j++){ r[i][j]=r[i][j-1]*10+s[j]-'0'; if(r[i][j]>T+1)r[i][j]=T+1; } for(int i=1;i<=len;i++){ int flag=0; for(int j=i;j<=len;j++) if(s[j]=='0'){ flag=1;break; } if(!flag)a[i]=1; } } void Dfs(int now,int num,int s,int c,int pre){ if(s>T||num>sum)return; if(now==len){ if(s+c*r[pre+1][now]==T)falg=1; return; } if(a[now-1]&&s+c>T)return; Dfs(now+1,num+1,s,c*r[pre+1][now],now);// * Dfs(now+1,num+1,s+c*r[pre+1][now],1,now);// + Dfs(now+1,num,s,c,pre);// kong } int main() { //freopen("puzzle.in","r",stdin); //freopen("puzzle.out","w",stdout); while(1){ ans=20;falg=0; memset(r,0,sizeof(r)); memset(a,0,sizeof(a)); scanf("%s%d",s+1,&T); if(T<0)break; len=strlen(s+1); Solve(); int l=0,r=len-1; while(l<=r){ falg=0; sum=(l+r)/2; Dfs(1,0,0,1,0); if(falg){ r=sum-1; ans=sum; } else l=sum+1; } if(ans==20)printf("-1 "); else printf("%d ",ans); } return 0; }
Problem 3花园的守护之神(greendam.*)
题目描述
看着正在被上古神兽们摧残的花园,花园的守护之神――小Bug同学泪流满面。然而,FZOI不相信眼泪,小bug与神兽们的战争将进行到底!
通过google,小Bug得知,神兽们来自遥远的戈壁。为了扭转战局,小Bug决定拖延神兽增援的速度。从戈壁到达花园的路径错综复杂,由若干段双向的小路组成。神兽们通过每段小路都需要一段时间。小Bug可以通过向其中的一些小路投掷小xie来拖延神兽。她可以向任意小路投掷小Xie,而且可以在同一段小路上投掷多只小xie。每只小Xie可以拖延神兽一个单位的时间。即神兽通过整段路程的总时间等于没有小xie时他们通过同样路径的时间加上路上经过的所有小路上的小xie数目总和。
神兽们是很聪明的。他们会在出发前侦查到每一段小路上的小Xie数目,然后选择总时间最短的路径。小Bug现在很想知道最少需要多少只小Xie,才能使得神兽从戈壁来到花园的时间变长。作为花园中可爱的花朵,你能帮助她吗?
输入格式
第1行包括一个整数N,表示地图中路点的个数;一个整数M,表示小路个数;以及整数S和T,分别表示戈壁和花园的路点编号。N个路点分别被编号为自然数1~N。
以下M行,每行三个整数A、B和C,表示路点A和B之间有一条小路相连,且通过它需要的时间为C。
输入数据保证两路点间最多只有一条小路相连,且戈壁和花园的路点是连通的。
输出格式
一个整数,表示使S到T之间最短路增长所需要的最少的小xie的数目。
输入样例
5 5 1 5
1 2 1
2 3 3
1 4 2
4 3 2
5 1 2
输出样例
1
数据范围
对于30%的数据,满足N≤10,M≤50。
对于50%的数据,满足N≤200,M≤10000。
对于全部的数据,满足N≤1000,M≤499500,0<C≤1000000。
/*暴力30 正解网络流 不会....*/ #include<iostream> #include<cstdio> #include<cstring> #include<queue> #define maxn 1010 using namespace std; int n,m,num,head[maxn],S,T,dis[maxn],f[maxn],mx,sum,falg; struct node{ int v,t,pre,x; }e[maxn*maxn]; queue<int>q; int init(){ int x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } void Add(int from,int to,int dis){ e[num].v=to; e[num].t=dis; e[num].pre=head[from]; head[from]=num++; } int SPFA(){ while(!q.empty())q.pop(); memset(f,0,sizeof(f)); memset(dis,127/3,sizeof(dis)); dis[S]=0;f[S]=1; q.push(S); while(!q.empty()){ int k=q.front(); q.pop();f[k]=0; for(int i=head[k];i!=-1;i=e[i].pre){ int v=e[i].v; if(dis[v]>dis[k]+e[i].t+e[i].x){ dis[v]=dis[k]+e[i].t+e[i].x; if(f[v]==0){ f[v]=1;q.push(v); } } } } return dis[T]; } void Dfs(int now,int s){ if(s==sum||now==num){ if(SPFA()>mx)falg=1; return; } e[now].x++;e[now^1].x++; Dfs(now+2,s+1);if(falg)return; e[now].x--;e[now^1].x--; Dfs(now+2,s);if(falg)return; } int main() { freopen("greendam.in","r",stdin); freopen("greendam.out","w",stdout); n=init();m=init();S=init();T=init(); int u,v,t;memset(head,-1,sizeof(head)); for(int i=1;i<=m;i++){ u=init();v=init();t=init(); Add(u,v,t);Add(v,u,t); } mx=SPFA(); for(sum=0;sum<=m;sum++){ Dfs(0,0); if(falg)break; } printf("%d ",sum); return 0; }