考试7.15
烽火传递(signal.cpp)
【题目描述】
烽火台是重要的军事防御设施,一般建在交通要道或险要处。一旦有军情发
生,则白天用浓烟,晚上用火光传递军情。
在某两个城市之间有n座烽火台,每个烽火台发出信号都有一定的代价。为
了使情报准确传递,在连续m个烽火台中至少要有一个发出信号。
现在输入n和每个烽火台发出信号的代价ai,请计算总共最少需多少的代价
才能实现两城市之间准确传递情报。
【输入格式】
第一行是用空格隔开两个数 n 和 m;
第二行使用空格隔开的 n 个整数,表示每个烽火台发出信号的代价。
【输出格式】
输出仅一个整数,表示最小代价。
【数据规模与约定】
对于 30% 的数据,n,m≤10;
对于 50% 的数据,n,m≤1000;
对于 100%的数据,n,m≤\(2*10^5\),对所有数据有 1<=ai<=1000。
【Solution】
单调优先队列优化dp
考试时把弹出队尾的f[i]打成f[q[i]],只拿了10分,亏大了。
上代码啦!
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m;
int a[N],q[N],f[N];//以i为结尾的最小
inline int read() {
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9') {
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int main() {
// freopen("signal.in","r",stdin);
// freopen("signal.out","w",stdout);
n=read(),m=read();
for(int i=1; i<=n; i++) {
a[i]=read();
}
memset(f,0x3f,sizeof f);
int l=1,r=1;
f[0]=0;
for(int i=1; i<=n; i++) {
while(l<r&&q[l]+m<i)l++;
f[i]=f[q[l]]+a[i];
// cout<<l<<" "<<r<<" "<<i<<" :"<<f[i]<<endl;
while(l<r&&f[q[r]]>f[i])r--;
q[++r]=i;
}
int ans=0x3f3f3f3f;
for(int i=n-m+1; i<=n; i++) {
// cout<<i<<" "<<f[i]<<endl;
ans=min(ans,f[i]);
}
cout<<ans<<endl;
return 0;
}
小R与回家(home.cpp)
【题目描述】
小R在火星玩累了,打算回家, 这是一个时空旅行的故事。
小R要从火星(编号为1)回到地球(编号为n),宇宙中一共有n个星球,
星球之间有一些航线,但是有些星球间的航线可能导致时间倒流,并且星球
之间的航线并不保证可逆(即a到b的时间和b到a的时间不一定相同)。时空旅行
要求不能在出发时间之前到达最终目的地,否则会导致时空错乱,危及生命。
小R的飞船上有速度调节装置,可以用来改变星球间飞行的时间,即可以将
整次时空旅行中两两星球之间的时间增加或减少同一个整数。
小R很想家,你能帮助他求出最快回家的时间。
【输入格式】
输入文件包含多组数据,第1个数为T,表示数据组数。
对于每组数据,输入第1行为两个正整数n,m,为星球的个数和星球间的路
线数。
接下来m行,每行三个整数i,j和t, 表示由星球i到星球j飞行的时间为t。
由i到j最多只会有一条飞行线路。
【输出格式】
输出文件共T行,每组数据输出一行。
如果可以通过调节速度调节器完成任务,则输出一个非负整数,表示由星球1到星球n的最短时间(注意最短时间要大于或者等于0)。
如果不能由火星回到地球,则输出-1。
【数据规模与约定】
对于20% 的数据,n<=10,t>=0
另有20% 的数据,保证所有星球出度不超过1
另有20% 的数据,-100<=t<=100;
对于100%的数据:T<=10,n<=100,m<=n*(n-1),-100000<=t<=100000。
【Solution】
二分求增加减少值,每次跑一遍最短路,找最小非负整数
| k\t | 0 | 1 | 2 |
|---|---|---|---|
| 7 | -4 | 3 | 10 |
| 3 | -3 | 0 | 3 |
| 2 | -3 | -1 | 1 |
例如上图所求应为第4列第4行的1
-
考虑二分答案。因为图中含有负边权,所以用 spfa 来统计最短路的答案。
-
把不会到达的点处理掉然后对答案进行二分;
-
用check(x) 判断 x 是否可以作为边权的调整值
-
用count(i,x) 函数,扫描图中的负环,判断当前调整值情况下是否存在负环
-
如果没有负环,则用spfa(x) 计算在调整值为 x 的情况下的最短路;(因为图中含有负边权)
Code
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
const int N=105;
const int M=1e4+5;
int T;
int n,m;
//建图
int h[N],ver[M],nex[M],val[M],tot;
void add(int x,int y,int z)
{
ver[++tot]=y,val[tot]=z,nex[tot]=h[x],h[x]=tot;
}
int d[N],vis[N],k[N];
void init()
{
tot=1;
memset(h,0,sizeof h);
memset(ver,0,sizeof ver);
memset(nex,0,sizeof nex);
memset(val,0,sizeof val);
}
void spfa(int t)
{
memset(d,0x3f,sizeof d);
memset(vis,0,sizeof vis);
d[1]=0,vis[1]=1;
queue<int>q;
q.push(1);
while(q.size())
{
int u=q.front();q.pop();
for(int i=h[u];i;i=nex[i])
{
int v=ver[i];
if(!k[v])continue;
if(d[v]>d[u]+val[i]+t)
{
d[v]=d[u]+val[i]+t;
if(!vis[v])q.push(v),vis[v]=1;
}
}
vis[u]=0;
}
}
void dfs(int x)
{
if(vis[x])return ;
vis[x]=1;
for(int i=h[x];i;i=nex[i])
dfs(ver[i]);
}
int count(int x,int t)
{
vis[x]=1;
for(int i=h[x],v;i;i=nex[i])
{
if(!k[v=ver[i]])continue;
if(d[v]>d[x]+val[i]+t)
{
if(vis[v])return 1;
d[v]=d[x]+val[i]+t;
if(count(v,t))return 1;
}
}
vis[x]=0;
return 0;
}
bool check(int x)
{
for(int i=1;i<=n;i++)
{
if(!k[i])continue;
memset(vis,0,sizeof vis);
memset(d,0x3f,sizeof d);
if(count(i,x))return false;
}
spfa(x);
if(d[n]>=0) return true;
else return false;
}
int main()
{
T=read();
while(T--)
{
n=read();m=read();
int u,v,z;init();//预处理
for(int i=1;i<=m;i++)
{
u=read(),v=read(),z=read();
add(u,v,z);
}
memset(k,0x3f,sizeof k);
memset(vis,0,sizeof vis);
dfs(1);
for(int i=1;i<=n;i++)
{
if(!vis[i])k[i]=0;
}
for(int i=1;i<=n;i++)
{
if(!k[i])continue;
memset(vis,0,sizeof vis);
dfs(i);
if(!vis[n])k[i]=0;
}
//二分求增减值
int l=-100000,r=100000;
int ans=inf;
while(l<r)
{
int mid=(l+r)>>1;
// cout<<mid<<" :"<<l<<" "<<r<<" "<<d[n]<<endl;
if(check(mid))
{
ans=d[n];
r=mid;
}
else l=mid+1;
}
if(ans>=inf)printf("%d\n",-1);
else cout<<ans<<endl;
}
return 0;
}
疯狂的火神(crazy.cpp)
【题目描述】
火神为了检验zone的力量,他决定单挑n个人。
由于火神训练时间有限,最多只有t分钟,所以他可以选择一部分人来单挑,
由于有丽子的帮助,他得到了每个人特定的价值,每个人的价值由一个三元组
(a,b,c)组成,表示如果火神在第x分钟单挑这个人,他就会得到a-b*x的经验值,
并且他需要c分钟来打倒这个人。
现在火神想知道,他最多可以得到多少经验值,由于火神本来就很笨,进入
zone的疯狂的火神就更笨了,所以他希望你来帮他计算出他最多可以得到多少经
验值。
【输入格式】
第一行一个正整数T,表示数据组数
对于每组数据,第一行为两个正整数n和t,表示跟火神单挑的人的个数和火
神的训练时间。
下面n行,每行三个正整数Ai,Bi,Ci,表示每个人的价值,含义见题目。
【输出格式】
对于每组数据输出一行一个整数表示火神最多能得到多少经验值
【数据规模与约定】
对于20% 的数据:1≤n≤10
对于50% 的数据:1≤n≤18
对于100%的数据:1≤n≤1000,1≤t≤3000,1≤Ci≤t,Ai≤\(10^6\)
保证n>200的数据组数不超过五组,其他的数据组数不超过10组,保证每个
人贡献的经验值到训练结束都不会变成负数。
【Solution】
把\(b_i和c_i的比值排序\),01背包
考试的时候知道是dp,但完全想不到
改的时候,没有比较最大值,交了好几发才过,叹气.jpg(果然,我太废了)
代码又来啦!
#include<bits/stdc++.h>
using namespace std;
int T;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
const int N=1e3+5;
int n,m,f[N*3];
struct ww{
int a,b,c;
double d;
}pl[N];
bool cmp(ww x, ww y)
{
return x.d>y.d;
}
int main()
{
T=read();
while(T--)
{
n=read(),m=read();
for(int i=1;i<=n;i++)
{
pl[i].a=read(),pl[i].b=read();
pl[i].c=read();pl[i].d=1.0*pl[i].b/pl[i].c;
}
sort(pl+1,pl+n+1,cmp);
memset(f,0,sizeof f);
for(int i=1;i<=n;i++)
{
for(int j=m;j>=pl[i].c;j--)
{
f[j]=max(f[j],f[j-pl[i].c]+pl[i].a-pl[i].b*j);
}
}
int ans=0;
for(int i=1;i<=m;i++)ans=max(ans,f[i]);
cout<<ans<<endl;
}
return 0;
}
火神的鱼(fish.cpp)
【题目描述】
火神最爱的就是吃鱼了,所以某一天他来到了一个池塘边捕鱼。池塘可以看
成一个二维的平面,而他的渔网可以看成一个与坐标轴平行的矩形。
池塘里的鱼不停地在水中游动,可以看成一些点。有的时候会有鱼游进渔网,
有的时候也会有鱼游出渔网。所以火神不知道什么时候收网才可以抓住最多的鱼,
现在他寻求你的帮助。
他对池塘里的每条鱼都给予了一个标号,分别从1到n标号,n表示池塘里鱼
的总数。鱼的游动可以概括为两个动作:
1 l r d:表示标号在[l,r]这个区间内的鱼向x轴正方向游动了d个单位长度。
2 l r d:表示标号在[l,r]这个区间内的鱼向y轴正方向游动了d个单位长度。
在某些时刻火神会询问你,现在有多少条他关心的鱼在渔网内(边界上的也
算),请你来帮助他吧。
【输入格式】
第一行包含一个整数T,表示测试数据组数,对于每组测试数据:
第一行包含一个整数n表示鱼的总数。
第二行包含四个整数x1,y1,x2,y2,表示渔网的左下角坐标和右上角坐标。
接下来n行每行两个整数xi,yi,表示标号为i的鱼初始时刻的坐标。
接下来一行包含一个整数m,表示后面的事件数目。
接下来的m行,每行为以下三种类型的一种:
1 l r d:表示标号在[l,r]这个区间内的鱼向x轴正方向游动了d个单位长度。
2 l r d:表示标号在[l,r]这个区间内的鱼向y轴正方向游动了d个单位长度。
3 l r:表示询问现在标号在[l,r]这个区间内的鱼有多少在渔网内。
【输出格式】
对于每组数据的每个询问,输出一个整数表示对应的答案。
【数据规模与约定】
对于30%的数据1≤n,m≤1000
对于100%的数据1≤T≤10,1≤n,m≤30000,1≤l≤r≤n. 1≤d≤\(10^9\),x1≤x2,y1≤y2。
保证任意时刻所有涉及的坐标值在[\(−10^9,10^9\)]范围内。
【Solution】
线段树

浙公网安备 33010602011771号