8.18考试总结

题目 T1 T2 T3 T4
预计得分 100 100 100 ???(随便码的随机数)
实际得分 100 100 100 10

T1&&T2

板子,不写

T3 修理

一眼看上去好像是二维的,实际上发现只是要跑到所有电线杆再跑回来,转化为一维的Hamilton回路问题

我们使用BFS预处理电线杆与起点的距离,具体做法为:找出起点与所有的电线杆,每出现一次便存储起来,后面全部作为起点跑一次BFS,记录答案即可(这样能用【数据删除】大法优化时间)

后面直接跑状压板子即可

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e3+5,mod=1e9+7,inf=1e18;
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;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int n,m,t;
char c[maxn][maxn];
int dis[maxn][maxn];
bool vis[maxn][maxn];
int dp[1<<17][17];
struct node{
	int x, y;
}tmp[maxn];
int mp[maxn][maxn];
struct Node{
	int x,y,step;
};
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
bool check(int x,int y){
	if(c[x][y]=='#'||x<1||y<1||x>n||y>m){
		return 0;
	}
	return 1;
}
int num; 
void bfs(int xx,int yy){
	queue<Node>q;
	q.push({xx,yy,0});
	while(!q.empty()){
		num++;
		Node now=q.front();
		q.pop();
		if(vis[now.x][now.y]==1){//赛时因为这里写到了下面被卡了好久QAQ
			continue;
		}
		if(c[now.x][now.y]=='S'){
			dis[mp[xx][yy]][mp[now.x][now.y]]=dis[mp[now.x][now.y]][mp[xx][yy]]=now.step;
		}
		if(c[now.x][now.y]=='T'){
			dis[mp[xx][yy]][mp[now.x][now.y]]=dis[mp[now.x][now.y]][mp[xx][yy]]=now.step;
		}//记录答案
		vis[now.x][now.y]=1;
		for(int i=0;i<4;i++){
			int nx=now.x+dx[i];
			int ny=now.y+dy[i];
			if(check(nx,ny)){
				q.push({nx,ny,now.step+1});
			}
		}
	}
}
signed main(){
//	freopen("T3.in","r",stdin);
//	freopen("T3.out","w",stdout);
	cin>>n>>m>>t;
	int cnt=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>c[i][j];
			if(c[i][j]=='S'){
				mp[i][j]=0;
				tmp[0].x=i;
				tmp[0].y=j;
			}
			if(c[i][j]=='T'){
				cnt++;
				mp[i][j]=cnt;//离散化记录编号更方便状压的书写
				tmp[cnt].x=i;
				tmp[cnt].y=j;
			}
		}
	} 
	for(int i=0;i<=cnt;i++){//神奇的时间优化:O(16nm)
		memset(vis,0,sizeof(vis));
		bfs(tmp[i].x,tmp[i].y);
	}
	cnt++;
	memset(dp,0x3f3f3f3f,sizeof(dp));
	dp[1][0]=0;
	for(int sta=2;sta<(1<<cnt);sta++){
		for(int i=0;i<cnt;i++){
			if(sta&(1<<i)){
				for(int j=0;j<cnt;j++){
					int tmp=sta^(1<<i);
					if(i!=j&&(tmp&(1<<j))){
						dp[sta][i]=min(dp[sta][i],dp[tmp][j]+dis[j][i]);
					}
				}
			}
		}
	}
	int mini=1e9;
	for(int i=1;i<cnt;i++){
		mini=min(mini,dp[(1<<cnt)-1][i]+dis[i][0]);
	}
	cout<<mini+(t*(cnt-1));
	return 0;
}

T4 外送

看到数据范围果断打随机数写状压

为了解决这个问题,我们需要找到晓莱最多能完成的外卖配送任务数量。每个任务有特定的领取和送达时间限制,晓莱需要在这些时间范围内完成任务的领取和送达。我们可以使用动态规划结合状态压缩来高效地探索所有可能的任务完成顺序和路径。

方法思路

  1. 问题分析:晓莱从点1开始,需要完成多个配送任务,每个任务有起点、终点、最早领取时间和最晚送达时间。目标是在满足时间限制的前提下,最大化完成的任务数量。

  2. 关键思路:使用三进制状态压缩来表示每个任务的状态(未开始、已领取未送达、已完成)。动态规划状态dp[i][j]表示在点i处于状态j时的最小时间。

  3. 算法选择:

    • Floyd:预处理所有点对之间的最短路径,便于快速计算移动时间。
    • 状态DP:遍历所有可能的状态,对于每个状态和位置,尝试领取或送达任务,并更新状态和时间。随后尝试移动到其他点。
  4. 复杂度分析:状态数为3^Q * N,其中Q是任务数量,N是点数。每个状态处理时间为O(Q * N),总复杂度为O(3^Q * Q * N^2),在给定约束下可行。

如果状态从0变成1,此时需要判断是否到达预设的时间,还没有的话需要等待。

如果状态1变成了状态2,直接判断时间

解决代码

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=20+5,mod=1e9+7,inf=1e18;
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;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int n,m,q;
int dis[maxn][maxn];
struct Node{
	int s,t,l,r;
}a[maxn];
void floyd(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
			}
		}
	}
}
int third[maxn];
int dp[maxn][60005];
void init(){
	third[1]=1;
	for(int i=2;i<=q+1;i++){
		third[i]=third[i-1]*3;
	}
	return;
}
void solve(int max_sta){
	memset(dp,0x3f,sizeof(dp));
	dp[1][0]=0;
	for(int i=0;i<=max_sta;i++){
		for(int j=1;j<=n;j++){
			for(int k=1;k<=q;k++){
				int tmp=i%third[k+1]/third[k];
				if(tmp==0&&dp[j][i]+dis[j][a[k].s]<=a[k].r){
					dp[a[k].s][i+third[k]]=min(dp[a[k].s][i+third[k]],max(a[k].l,dp[j][i]+dis[j][a[k].s]));
				}
				if(tmp==1&&dp[j][i]+dis[j][a[k].t]<=a[k].r){
					dp[a[k].t][i+third[k]]=min(dp[a[k].t][i+third[k]],dp[j][i]+dis[j][a[k].t]);
				}
			}
		}
	}
	return;
}
signed main(){
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j){
				dis[i][j]=0;
			}
			else{
				dis[i][j]=0x3f3f3f3f;
			}
		}
	}
	int st,ed,val;
	while(m--){
		cin>>st>>ed>>val;
		dis[st][ed]=min(dis[st][ed],val);
	}
	for(int i=1;i<=q;i++){
		cin>>a[i].s>>a[i].t>>a[i].l>>a[i].r;
	}
	floyd();
	init();
	solve(third[q+1]-1);
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=third[q+1]-1;j++){
			if(dp[i][j]>=0x3f3f3f3f){
				continue;
			}
			int tmp=0;
			for(int k=1;k<=q;k++){
				tmp+=(((j%third[k+1]/third[k])==2)?1:0);
			}
			ans=max(ans,tmp);
		}
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2025-08-19 21:19  KK_SpongeBob  阅读(7)  评论(0)    收藏  举报