A:

题干:

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

小Hi要将一个01串S传输给小Ho,由于S非常长,所以小Hi决定用长度为N的2个数组A = [A1, A2, ..., AN]和B = [B1, B2, ..., BN]表示S。  

具体来讲,是指S由N段连续的字符串组成,其中第i段包含Ai个Bi。其中Bi可能是0或1。  

例如 A = [1, 2, 3, 4], B = [1, 0, 0, 1]表示S = "1000001111"。  

现在小Ho想把S变成一个01交错的字符串。请你帮他计算他最少要改变S中多少个字符才能达成?

输入

第一行包含一个整数N。  

第二行包含N个整数,A1, A2, A3, ... AN。  

第三行包含N个整数,B1, B2, B3, ... BN。  

1 <= N <= 100000  

1 <= Ai <= 100000  

0 <= Bi <= 1

输出

一个整数代表答案

样例输入

4  
1 2 3 4  
1 0 0 1

样例输出

4

解题报告:

 

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
int a[MAX],b[MAX];
int main()
{
	int n;
	cin>>n;
	ll sum = 0;
	for(int i = 1; i<=n; i++) scanf("%d",a+i),sum += a[i]/2;
	for(int i = 1; i<=n; i++) scanf("%d",b+i);
	//开头是1 
	ll ans1 = 0;
	bool sd = 1;
	for(int i = 1; i<=n; i++) {
		if(b[i] == sd) {
			ans1 += a[i]/2;
			if(a[i]&1) sd = !sd;
		}
		else {
			ans1 += (a[i]+1)/2;
			if(a[i]&1) sd = !sd; 
		}
	}
	ll ans2 = 0;
	
	sd = 0;
	for(int i = 1; i<=n; i++) {
		if(b[i] == sd) {
			ans2 += a[i]/2;
			if(a[i]&1) sd = !sd;
		}
		else {
			ans2 += (a[i]+1)/2;
			if(a[i]&1) sd = !sd; 
		}
	}	
	printf("%lld\n",min(ans1,ans2));

	return 0 ;
 }

B:

题干:

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

小Hi有NxM的方格矩阵,每个方格的高度是Hij。例如如下是2x3的方格矩阵和每个方格的高度:

-->    1 3 4
 左    2 5 3
 视      ^
 图      |
       前视图

左边看过去,可以得到方格矩阵的左视图L = [L1 ... LN];从前边看过去,可以得到方格矩阵的前视图F = [F1 ... FM]。  

例如上例中L = [4, 5], F = [2, 5, 4]。

现在小Ho不知道每个方格的高度,只知道这些方格的左视图和前视图。他发现只知道左视图和前视图并不一定能唯一确定一个方格矩阵。

例如

2 4 4
2 5 4

的左视图和前视图也是L = [4, 5]和F = [2, 5, 4]。  

于是小Ho想知道,对于所有可能的方格矩阵,格子高度之和最大是多少。

输入

第一行包含两个整数N和M。  

第二行包含N个整数L1, L2, ... LN,代表左视图。  

第三行包含M个整数F1, F2, ... FM,代表前视图。

1 <= N, M <= 1000  

1 <= Li, Fi <= 1000

输出

一个整数代表答案

样例输入

2 3  
4 5  
2 5 4

样例输出

21

解题报告:

N*M暴力跑就行了。枚举每一个看他可以达到的最大值。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
int n,m;
int h[MAX],q[MAX];
int main()
{
	ll ans = 0;
	cin>>n>>m;
	for(int i = 1; i<=n; i++) cin>>h[i];
	for(int i = 1; i<=m; i++) cin>>q[i];
	for(int i = 1; i<=n; i++) {
		for(int j = 1; j<=m; j++) {
			ans += min(h[i],q[j]);
		}
	}
	cout <<ans;

	return 0 ;
 }

C:

题干:

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

给定N个整数A1, A2, ... AN。现在小Ho可以任意从中取出两个整数X,Y凑成一个数对(X, Y),只要满足Y = 2X。  

如果每个Ai最多被取出一次,请你帮小Ho计算他最多能凑出多少个数对?

输入

第一行包含一个整数N。  

第二行包含N个整数A1, A2, ... AN。  

1 <= N <= 100000  

1 <= Ai <= 100000

输出

一个整数代表答案

样例输入

5  
1 2 4 8 16

样例输出

2

解题报告:

   这题数据范围没有加满,所以可以用数组来计数(AC代码1),这题Ai加到1e18也可以做(AC代码2)。

AC代码1:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll ans=0;
int n;
int a[100005];
int cnt[100005];
int main() {
	scanf("%d",&n);
	for(int i=0; i<n; i++)scanf("%d",&a[i]),cnt[a[i]]++;
	sort(a,a+n);
	for(int i=0; i<n; i++) {
		if(a[i]%2==0&&cnt[a[i]/2]) {
			ans++;
			cnt[a[i]/2]--;
			cnt[a[i]]--;
		}
	}
	cout<<ans;
	return 0;
}

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
multiset<int> ms;
int a[MAX],n;
int main()
{
	int ans = 0;
	cin>>n;
	for(int i = 1; i<=n; i++) {
		cin>>a[i];
		ms.insert(a[i]);
	}
	auto it = ms.begin();
	for(int i = 1; i<=n/2; i++) {	
		while(ms.find((*it)*2) == ms.end() && it != ms.end()) ++it;
		if(it != ms.end()) {
			auto itt = ms.find((*it)*2);
			ans++;
			ms.erase(itt);
			itt = it;
			++it;
			ms.erase(itt);
		}
		else break;
 	}
 	cout <<ans;
	return 0 ;
 }

D:

题干:

时间限制:20000ms

单点时限:2000ms

内存限制:256MB

描述

H市的土地如下图所示,呈现NxM块区域,每一块区域都有自己的高度Hij。

    +---+---+---+---+---+---+
    |H11|H12|H13|H14|...|H1M|
    +---+---+---+---+---+---+
    |H21|H22|H23|H24|...|H2M|
    +---+---+---+---+---+---+
          . . . . . . .
    |HN1|HN2|HN3|HN4|...|HNM|
    +---+---+---+---+---+---+

为了使以后的城市交通比较便利,H市决定将土地进行平整,使得所有的区域高度相等。已知将1个单位的土地移动到相邻的区域需要花费1的费用,同时当前区域的高度降低1,接收土地的区域高度增加1。那么将所有的区域调整到相同的高度所需的最小费用是多少呢?

输入

第一行包含两个整数N和M。  

以下N行包含一个NxM的矩阵H。

0 <= Hij <= 1000

1 <= N, M <= 50

输出

一个整数代表答案

样例输入

2 2
3 4
6 7

样例输出

4

解题报告:

   一眼网络流。先计算出最终平衡时所有点的高度值sum(可以计算出,相当于已知)。建图:建一个起点连向所有可以高度减少的点,流量是h[i][j]-sum(因为他必须减少),费用是0;新建一个汇点,高度需要增加的点连向汇点,流量是sum-h[i][j],费用是0,那么这样建图首先保证了源点流出的流量==汇点流入的流量,这也就保证了流量不会丢失,也就是最大流就是这么大,所以满足了我们需要求最小费用,的要求。相当于是我通过建图,首先固定了最大流(因为新建的源点到新建的汇点,最大流肯定就是min(源点连出的所有的边的流量之和,汇点流入的所有的边的流量之和),而这两者相同,所以最大流已经确定了,就是源点连出的所有边的流量之和,也就是源点流出的流量),然后跑模板去求最小费用就行了。

不过这题他数据错了你敢信,,,给的范围根本不是50*50的,,而且远大于这个,,怪不得一直RE。。。

另一种建图方式:起点到每一个点都有一个流量h[i][j]费用0的边,每个点都到终点有一个流量sum费用0的边,然后每个点向四周连边。合法性的证明参考上一种建图方式。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;

const int MAXN = 70000;
const int MAXM = 100005;
const int INF = 0x3f3f3f3f;
struct Edge {
	int to,next,cap,flow,cost;
} edge[MAXM];
int head[MAXN],tol;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int n,m;
int N;//节点总个数,节点编号从 0 ~ N-1
void init(int n) {
	N = n;
	tol = 0;
	memset(head, -1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost) {
	edge[tol].to = v;
	edge[tol].cap = cap;
	edge[tol].cost = cost;
	edge[tol].flow = 0;
	edge[tol].next = head[u];
	head[u] = tol++;
	edge[tol].to = u;
	edge[tol].cap = 0;
	edge[tol].cost = -cost;
	edge[tol].flow = 0;
	edge[tol].next = head[v];
	head[v] = tol++;
}
bool spfa(int s,int t) {
	queue<int>q;
	for(int i = 0; i <= N; i++) {
		dis[i] = INF;
		vis[i] = false;
		pre[i] = -1;
	}
	dis[s] = 0;
	vis[s] = true;
	q.push(s);
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = false;
		for(int i = head[u]; i !=-1; i = edge[i].next) {
			int v = edge[i].to;
			if(edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost ) {
				dis[v] = dis[u] + edge[i].cost;
				pre[v] = i;
				if(!vis[v]) {
					vis[v] = true;
					q.push(v);
				}
			}
		}
	}
	if(pre[t] ==-1)return false;
	else return true;

}
//返回的是最大流,cost 存的是最小费用
int minCostMaxflow(int s,int t,int &cost) {
	int flow = 0;
	cost = 0;
	while(spfa(s,t)) {
		int Min = INF;
		for(int i = pre[t]; i !=-1; i = pre[edge[i^1].to]) {
			if(Min > edge[i].cap-edge[i].flow)
				Min = edge[i].cap-edge[i].flow;
		}
		for(int i = pre[t]; i !=-1; i = pre[edge[i^1].to]) {
			edge[i].flow += Min;
			edge[i^1].flow-= Min;
			cost += edge[i].cost * Min;
		}
		flow += Min;
	}
	return flow;
}
int nx[]={0,1,0,-1};
int ny[]={1,0,-1,0};
int id(int i,int j) {
	return (i-1)*m+j;
}
int h[555][555];
int main() 
{
	while(~scanf("%d%d",&n,&m)) {
		init(n*m+22);
		int st=0,ed=n*m+1,sum = 0;	
		for(int i = 1; i<=n; i++) {
			for(int j = 1; j<=m; j++) {
				scanf("%d",&h[i][j]);sum += h[i][j];
			}
		}
		sum /= (n*m);
		for(int i = 1; i<=n; i++) {
			for(int j = 1; j<=m; j++) {
				if(h[i][j] > sum) addedge(st,id(i,j),h[i][j] - sum,0);
				else if(h[i][j] < sum) addedge(id(i,j),ed, sum - h[i][j],0);

			}
		}
		for(int i = 1; i<=n; i++) {
			for(int j = 1; j<=m; j++) {
				for(int k = 0; k<4; k++) {
					int tx = i + nx[k];
					int ty = j + ny[k];
					if(tx<1||tx>n||ty<1||ty>m) continue;
					addedge(id(i,j),id(tx,ty),INF,1);
				}		
			}
		}		
		int cost;
		int ans = minCostMaxflow(st,ed,cost);
		printf("%d\n",cost);
	}
	return 0 ;
}