BUCT2020春季学期ACM周赛-11
Posted leaflove
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BUCT2020春季学期ACM周赛-11相关的知识,希望对你有一定的参考价值。
BUCT2020春季学期ACM周赛-11
A-冲浪游戏(cf 900)
思路
贪心,本题考虑求解最小的扣款钱数,所以容易想到本体题按扣款的钱数从大到小排序(尽可能把扣款最多的游戏放到执行队列中,以减少扣款钱数),那么在按顺序遍历每个游戏,考虑游戏的执行时间,将当前游戏放到截止时间之前的时间段的最后一个未占用时间段内,如果无法找到一个时间段,那么将此游戏放到全时间段的最后一个未占用时间段(因为放在哪都是要扣款的,所以尽可能往后放,为其他游戏腾出时间)。
Code
/****************************************************
/@Author: Kirito
/@TIME: 2020-04-30
/@FILENAME: chonglang.cpp
/@REMARK:
/****************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn=1000;
int n,m,book[maxn];
struct game{
int t,w;
bool operator < (const game &x)const{
return this->w>x.w;
}
}arr[maxn];
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
FAST;
cin>>m>>n;
for(int i=1;i<=n;i++){
cin>>arr[i].t;
}
for(int i=1;i<=n;i++){
cin>>arr[i].w;
}
sort(arr+1,arr+n+1);
for(int i=1;i<=n;i++){
bool fl=true;
for(int j=arr[i].t;j>=1;j--){
if(book[j]==0){
book[j]=1;fl=false;break;
}
}
if(fl){
m-=arr[i].w;
for(int j=n;j>=1;j--)
if(book[j]==0){
book[j]=1;break;
}
}
}
cout<<m<<endl;
return 0;
}
B-最短Hamiton路径(cf 1400)
思路
用二进制上的数代表一个点的状态,取(1)或不取(0)。题目让求从点1到n的最短汉密顿路径,即经过每个点一次,这时的状态用二进制表示就是 ((1<<n)-1 (n个1))。用(dp[i][j])表示在状态 (i) 下,从(1)到 (j) 的最短汉密顿路径。
(dp[i][j])可由上一个状态(上一状态就是把 j从当前状态中去掉)(dp[i^(1<<(j-1))][k])得到,其中保证(k)是中存在的点,即 $ (i>>k) & 1 $。
表示$ i$ 的第 (k) 位是(1),即经过点 (k)。 注意是$ i>>k$ 不是 (i<<k)
则状态转移方程为:(dp[i][j]=min{dp[i^(1<<j)][k]+Map[k][j]}(k=1~n)); 其含义就是枚举到达点(j)之前的前一个点(k),取其最短。
Code
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#define Inf 0x3f3f3f
#define ll long long
using namespace std;
int dp[(1<<20)+5][25];
int maze[25][25];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%d",&maze[i][j]);
memset(dp,Inf,sizeof(dp));
dp[1][0]=0;
for(int i=1;i<=(1<<n)-1;i++)
{
for(int j=0;j<n;j++)
{
if((i>>j)&1)//如果i的第j位是1,也就是如果经过点j
{
for(int k=0;k<n;k++)
{
if((i>>k)&1)//如果i的第k位是1,也就是如果经过点k
{
dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+maze[k][j]);
}
}
}
}
}
printf("%d
",dp[(1<<n)-1][n-1]);
return 0;
}
C-假设电话线(cf 1600)
思路
二分加最短路,对于本题的结果,可以发现具有但单调性,所以我们可用二分来枚举结果,然后对原图进行加工,对于大于二分值的权边,置为1,否则置为0,然后用Dijkstra计算最短路判断最后到n的值是否大于k。
Code
/****************************************************
/@Author: Kirito
/@TIME: 2020-05-03
/@FILENAME: telephonlines.cpp
/@REMARK:
/****************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn=5000;
//information
int n,m,k;
//graph1
int head1[maxn],next1[maxn],v[maxn],w[maxn],cnt;
//dijk
int dist[maxn],book[maxn];
void add(int x,int y,int z){
v[++cnt]=y;w[cnt]=z;
next1[cnt]=head1[x];head1[x]=cnt;
return;
}
bool Dijkstra(int bl){
CSE(dist,INF);CSE(book,0);
priority_queue<pii,vector<pii>,greater<pii>> box;
dist[1]=0;box.push(make_pair(dist[1],1));
while(!box.empty()){
int x=box.top().second;int d=box.top().first;box.pop();
if(book[x]) continue;
book[x]=1;
for(int i=head1[x];i!=-1;i=next1[i]){
int y=v[i];int kk=(w[i]>=bl?1:0);
if(dist[y]>kk+d){
dist[y]=kk+d;
box.push(make_pair(dist[y],y));
}
}
}
return dist[n]>k;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
FAST;
CSE(head1,-1);CSE(next1,-1);
cin>>n>>m>>k;
for(int i=0;i<m;i++){
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);add(y,x,z);
}
int l=0,r=1000001,ans=-1;
while(l<=r){
int mid=(l+r)/2;
if(Dijkstra(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
if(ans>1000000) ans=-1;
if(r<0) ans=0;
cout<<ans<<endl;
return 0;
}
D-农场派对(cf 1500)
思路
对于去时的最短路径直接用Dijkstra算法求解即可,对于回来时的最短路径,将所有的有向边方向反转,权值不改变,之后按去时的路径求解最短路径,将两个最短路径的值加和就是最短的来回路径,遍历所有顶点求取最短路径最大值即可。
Code
/****************************************************
/@Author: Kirito
/@TIME: 2020-04-30
/@FILENAME: fanxiangbian.cpp
/@REMARK:
/****************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn=111111;
//graph1
int first[maxn],nxt1[maxn],u1[maxn],v1[maxn],w[maxn];
int cnt,n,m,s;
//graph2
int head[maxn],nxt2[maxn],u2[maxn],v2[maxn];
//dij
int dist1[maxn],dist2[maxn];
//邻接表
void add(int x,int y,int d){
u1[++cnt]=x;v1[cnt]=y;
u2[cnt]=y;v2[cnt]=x;
w[cnt]=d;
nxt1[cnt]=first[u1[cnt]];first[u1[cnt]]=cnt;
nxt2[cnt]=head[u2[cnt]];head[u2[cnt]]=cnt;
return;
}
void Dijkstra1(int s){
CSE(dist1,INF);
priority_queue<pii,vector<pii>,greater<pii>> box;
dist1[s]=0;box.push(make_pair(0,s));
while(!box.empty()){
int x=box.top().second;
int d=box.top().first;
box.pop();
for(int i=first[x];i!=-1;i=nxt1[i]){
int y=v1[i];
if(dist1[y]>d+w[i]){
dist1[y]=d+w[i];
box.push(make_pair(dist1[y],y));
}
}
}
return;
}
void Dijkstra2(int s){
CSE(dist2,INF);
priority_queue<pii,vector<pii>,greater<pii>> box;
dist2[s]=0;box.push(make_pair(0,s));
while(!box.empty()){
int x=box.top().second;
int d=box.top().first;
box.pop();
for(int i=head[x];i!=-1;i=nxt2[i]){
int y=v2[i];
if(dist2[y]>d+w[i]){
dist2[y]=d+w[i];
box.push(make_pair(dist2[y],y));
}
}
}
return;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
FAST;
CSE(first,-1);CSE(nxt1,-1);CSE(head,-1);CSE(nxt2,-1);
cin>>n>>m>>s;
for(int i=0;i<m;i++){
int x,y,d;
cin>>x>>y>>d;
add(x,y,d);
}
Dijkstra1(s);
Dijkstra2(s);
int ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,dist1[i]+dist2[i]);
}
cout<<ans<<endl;
return 0;
}
E-Roadblocks(cf 1400)
思路
Dijkstra次短路径板题,用一个数组(dist1[])记录最短路径,另一个数组(dist2[])记录次短路径,执行Dijkstra算法。
当要更新最短路时执行
- 更新次短路径为当前最短路径的值
- 更新最短路径为新的最短路径的值
当不需要更新最短路但是新的路径长度大于最短路径,但是小于次短路径时执行
- 更新次短路径为新的路径值
Code
/****************************************************
/@Author: Kirito
/@TIME: 2020-04-30
/@FILENAME: cidunalu.cpp
/@REMARK:
/****************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn=211111;
//graph
int first[maxn],nxt[maxn],w[maxn],u[maxn],v[maxn];
int n,m,cnt;
//dijkstra
int dis[maxn],ans[maxn];
//邻接表
void add(int x,int y,int d){
u[++cnt]=x;v[cnt]=y;w[cnt]=d;
nxt[cnt]=first[u[cnt]];first[u[cnt]]=cnt;
return;
}
void Dijkstra(int s){
CSE(dis,INF);CSE(ans,INF);
priority_queue<pii,vector<pii>,greater<pii>> box;
dis[s]=0;box.push(make_pair(0,s));
while(!box.empty()){
int x=box.top().second;
int d=box.top().first;
box.pop();
for(int i=first[x];i!=-1;i=nxt[i]){
int y=v[i];
if(dis[y]>d+w[i]){
ans[y]=dis[y];
dis[y]=d+w[i];
box.push(make_pair(dis[y],y));
}
else if(dis[y]==d+w[i]) continue;
else if(ans[y]>d+w[i]&&dis[y]<d+w[i]){
ans[y]=d+w[i];
box.push(make_pair(ans[y],y));
}
}
}
return;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
FAST;
CSE(first,-1);CSE(nxt,-1);
cin>>n>>m;
for(int i=0;i<m;i++){
int x,y,d;
cin>>x>>y>>d;
add(x,y,d);add(y,x,d);
}
Dijkstra(1);
cout<<ans[n]<<endl;
return 0;
}
F-最短路计数(cf 1400)
思路
Dijkstra简单变体,在计算最短路时,考虑两种情况,当需要更新最短路时,将当前节点的最短路径个数等于出发节点的最短路径个数,当不需要更新最短路径,但是当前路径和最短路径的值相等时,将当前最短节点的最短路径的个数加上出发节点的最短路径的个数。
Code
/****************************************************
/@Author: Kirito
/@TIME: 2020-04-30
/@FILENAME: counzuiduanlu.cpp
/@REMARK:
/****************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn=411111;
//geaph
int first[maxn],nxt[maxn],u[maxn],v[maxn];
int n,m,cnt;
//dij
ll dist[maxn],ct[maxn],book[maxn];
//邻接表
void add(int x,int y){
u[++cnt]=x;v[cnt]=y;
nxt[cnt]=first[u[cnt]];first[u[cnt]]=cnt;
return;
}
void Dijkstra(int s){
CSE(dist,INF);CSE(ct,0);CSE(book,0);
priority_queue<pii,vector<pii>,greater<pii>> box;
dist[s]=0;ct[s]=1;box.push(make_pair(0,s));
while(!box.empty()){
int x=box.top().second;
ll d=box.top().first;
box.pop();
if(book[x]) continue;
book[x]=1;
for(int i=first[x];i!=-1;i=nxt[i]){
int y=v[i];
if(dist[y]>d+1){
dist[y]=d+1;
ct[y]=ct[x];
box.push(make_pair(dist[y],y));
}
else if(dist[y]==d+1){
ct[y]=(ct[y]+ct[x])%100003;
}
}
}
return;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
CSE(first,-1);CSE(nxt,-1);
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
Dijkstra(1);
for(int i=1;i<=n;i++){
printf("%lld
",ct[i]%100003);
}
return 0;
}
G-最优贸易(cf 1600)
思路
本题有点类似于“农场派对”那题,因为要求每条路径上最大权和最小权之差,所以,对于最小权我们修改Dijkstra来计算每条路径上的最小权值(这里要遍历所有路径,所以对于每个顶点我们都要加入队列进行扩展),之后我们将图中的路径方向取反,然后原点改为(n),我用新得到的图计算最大权,两遍Dijkstra之后对于所有节点取每个节点处最大权减最小权的最大值即可(注意判断是否会到达该点)。对于这题可以用SPFA代替Dijkstra,可以有一定优化。
Code
/****************************************************
/@Author: Kirito
/@TIME: 2020-05-03
/@FILENAME: businessbest.cpp
/@REMARK:
/****************************************************/
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define CSE(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
#define Abs(x) (x>=0?x:(-x))
#define FAST ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll , ll> pll;
const int maxn=1111111;
//information
int n,m,city[maxn];
//graph1
int head1[maxn],next1[maxn],v1[maxn],cnt1;
//graph2
int head2[maxn],next2[maxn],v2[maxn],cnt2;
//dij
int dist1[maxn],book1[maxn],dist2[maxn],book2[maxn];
//邻接表的建立
void add1(int x,int y){
v1[++cnt1]=y;
next1[cnt1]=head1[x];head1[x]=cnt1;
return;
}
void add2(int x,int y){
v2[++cnt2]=y;
next2[cnt2]=head2[x];head2[x]=cnt2;
return;
}
void Dijkstra1(int s){//取最小
memcpy(dist1,city,sizeof(city));CSE(book1,0);
priority_queue<pii,vector<pii>,greater<pii>> box;
box.push(make_pair(dist1[s],s));
while(!box.empty()){
int minn=box.top().first;int x=box.top().second;box.pop();
if(book1[x]) continue;
book1[x]=1;
for(int i=head1[x];i!=-1;i=next1[i]){
int y=v1[i];
if(dist1[y]>minn) dist1[y]=minn;
box.push(make_pair(dist1[y],y));
}
}
return;
}
void Dijkstra2(int s){//取最大
memcpy(dist2,city,sizeof(city));CSE(book2,0);
priority_queue<pii,vector<pii>,greater<pii>> box;
box.push(make_pair(dist2[s],s));
while(!box.empty()){
int maxx=box.top().first;int x=box.top().second;box.pop();
if(book2[x]) continue;
book2[x]=1;
for(int i=head2[x];i!=-1;i=next2[i]){
int y=v2[i];
if(dist2[y]<maxx) dist2[y]=maxx;
box.push(make_pair(dist2[y],y));
}
}
return;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
FAST;
CSE(head1,-1);CSE(head2,-1);CSE(next1,-1);CSE(next2,-1);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>city[i];
}
for(int i=0;i<m;i++){
int x,y,z;
cin>>x>>y>>z;
add1(x,y);add2(y,x);
if(z==2){
add1(y,x);add2(x,y);
}
}
Dijkstra1(1);
Dijkstra2(n);
int ans=0;
for(int i=1;i<=n;i++){
if(book1[i]&&book2[i]){
ans=max(ans,dist2[i]-dist1[i]);
}
}
cout<<ans<<endl;
return 0;
}
以上是关于BUCT2020春季学期ACM周赛-11的主要内容,如果未能解决你的问题,请参考以下文章
2020-3-14 acm训练联盟周赛Preliminaries for Benelux Algorithm Programming Contest 2019 解题报告+补题报告
BUCT - 2021-2022-1 ACM集训队每周程序设计竞赛题解