Codeforces Round #677 (Div. 3)

F. Zero Remainder Sum

题意

给定一个 \(n \times m\) 的矩阵,你可以在每一行选择不多于 \(\frac{n}{2}\) 个元素, 使得整体选择的元素的和模 \(k\) 为0, 并且和越好。

\[1 \leq n, m, k \leq 70 \]

\[1 \leq a_{i j} \leq 70 \]

分析

然后一般情况是第j个数字从j-1的对应转移过来,类似背包。但是这题这么做的话对于模数r的转移对应的要求的是(x%k+a[i][j]%k)%k=r的这个x。

方便一点的话转化成顺推。即考虑第j+1个数字选还是不选。

对于第i行,第j + 1个数字
1、选择第j + 1个数字 dp[i][j + 1][l][(r + a[i][j + 1]) % k] = dp[i][j][l - 1][r] + a[i][j+1];
2、不选择第j + 1个数字 dp[i][j + 1][l][r] = dp[i][j][l][r];

同时这个题注意上下层的关系也要转移过来,因为下一层的状态选择需要上一层的决策。

然后每次更新下一层的第0位,就表示上一层模数为r时的最大值

dp[i + 1][0][0][r] = max(dp[i][j + 1][l][r], dp[i + 1][0][0][r]);

答案就是 dp[n + 1][0][0][0] : 第n+1行,第0位,取0个数字,模数为0的总和

代码

inline int read()
{
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
const int maxn=80;
int dp[maxn][maxn][maxn][maxn];
int a[maxn][maxn];
main(void)
{
	int n=read();
	int m=read();
	int k=read();
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		a[i][j]=read();
	}
	
  	memset(dp,-1,sizeof(dp));
  	dp[1][0][0][0]=0;

	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<m;j++)
		{
			for(int cnt=0;cnt<=min(j+1,m/2);cnt++)
			{
				for(int p=0;p<=k-1;p++)
				{
					dp[i][j+1][cnt][p]=max(dp[i][j+1][cnt][p],dp[i][j][cnt][p]);
					if(cnt>=1&&dp[i][j][cnt-1][p]!=-1)
					{
						dp[i][j+1][cnt][(p+a[i][j+1])%k]=max(dp[i][j+1][cnt][(p+a[i][j+1])%k],dp[i][j][cnt-1][p]+a[i][j+1]);
					} 
				}
				for(int p=0;p<k;p++)
				dp[i+1][0][0][p]=max(dp[i+1][0][0][p],dp[i][j+1][cnt][p]);
			}
		}
	}
	cout<<max(0,dp[n+1][0][0][0]); 
	
}

G. Reducing Delivery Cost

题意

给定一张 n 个点 m 条边的带权无向图,记 d(u,v) 为 u 到 v 的最短路,给定 k 个点对 (ai,bi)。你可以使任意一条边的边权变为 0,求\(∑d(ai,bi)\)的最小值

思路

思路:开始看到了路线免费,以为是出了分层最短路板子,看了看发现只让一条路免费。

开始直接考虑暴力,枚举每条边,然后每个点进行dijkstra,最后取最小。复杂度O(n^2klogm);

考虑一下对每个点其实可以先预处理,提前处理好每个点的对应的最短路。O(n^2logm)

考虑免费的边的贡献。

对每一个ki来说,边(a,b)有三种情况。

1.开始不在其最短路路径上,免费后也不在其最短路路径上。

2.开始不在其最短路路径上,免费后在其最短路路径上。

3.开始在其最短路路径上,免费后在其最短路路径上。

对于第一种情况,ki的最短路长度不变,仍然是原来的dis[ki.first][ki.second] (ki.first和ki.second分别代表其起点和终点)

对于第二三种情况,ki的最短路长度可能变化,dis[ki.first][a]+dis[ki.second][b];dis[ki.first][b]+dis[ki.second][a];

在这里插入图片描述

那么枚举边,再枚举每个k,求出总和的最小.

总时间复杂度O(mk+n^2logm)

代码

#include<bits/stdc++.h>
#define re register
#define inf 1e18
#define int long long
using namespace std;
struct edge
{
    int to,cost;
};
vector<edge>g[5005];//定义路径结构体
vector<edge>p[5005];
int n,m,k;
int dis[5005];
int d[1200][1200];
struct node//定义堆结构体
{
	//(如果看不懂)https://www.cnblogs.com/ZERO-/p/9347296.html
	int u,d;
	bool operator<(const node&rhs)const
	{
		return d>rhs.d;
	}
};
inline void djs(int s)
{
	for(re int i=1;i<=n;i++)dis[i]=inf;
	dis[s]=0;
	priority_queue<node>Q;//初始化
	
	node a ={s,0};
	Q.push(a);//第一个node
	
	while(!Q.empty())
	{
		node fr=Q.top();Q.pop();
		int u=fr.u,d=fr.d;
		//取出并记录
		if(d!=dis[u])continue;//避免处理无用数据,也就是dis[u]已经更新,之前未更新数据直接出栈,比如有一组数据 2 5,但是后面又入栈一组数据2 3,则2 5是无用数据
		for(re int j=0;j<g[u].size();j++)
		{
			int tm=g[u][j].to;
			if(dis[u]+g[u][j].cost<dis[tm])
			{			 
            	dis[tm]=dis[u]+g[u][j].cost;
				Q.push((node){tm,dis[tm]});
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		d[i][s]=dis[i];
		d[s][i]=dis[i];
	//	printf("##%d %d %d\n",i,s,d[i][s]);
	}
}
inline int read()
{
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
main(void)
{
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
		d[i][j]=inf;
	for(int i=1;i<=n;i++)
		d[i][i]=0;
	int x,y,z;
	vector<pair<int,int>>e1,e2;
	for (re int i=1;i<=m;i++)
	{
        edge tmp;
      	cin>>x>>y>>z;
      	tmp.cost=z;
      	tmp.to=y;
        g[x].push_back(tmp);   
        tmp.to=x;
        g[y].push_back(tmp);
        e1.push_back({x,y});
    }
    for(int i=1;i<=k;i++)
    {
    	int x=read();
    	int y=read();
    	edge tmp;
    	tmp.to=y;
		p[x].push_back(tmp);
		e2.push_back({x,y});
	}
	for(int i=1;i<=n;i++)
	{
		djs(i);
	}
	int ans=inf;
	for(auto e:e1)
	{
		int x=e.first,y=e.second;
		int index=0;
		for(auto pa:e2)
		{
			int m=pa.first,n=pa.second;
			index+=min(d[m][n],min(d[m][x]+d[y][n],d[m][y]+d[x][n]));;
		}
		ans=min(ans,index);
	}
	cout<<ans;
}

posted @ 2020-10-23 20:02  王乾宇  阅读(140)  评论(0编辑  收藏  举报