题目感受:
T5 的最小费用流不会,学了几天只是勉强听懂了的程度
综合前几套来看,不熟练 完全不会 的知识点有:状压DP、费用流
201412-1 门禁系统
简单题,直接放代码:
#include <cstdio>
#include <iostream>
using namespace std;
const int maxn=1e3+5;
int n,x,a[maxn];
int main()
{
for(cin>>n;n;n--){
cin>>x;
cout<<++a[x]<<' ';
}
return 0;
}
201412-2 Z字形扫描
模拟题,没啥技巧可言,直接给出代码:
#include <cstdio>
#include <iostream>
using namespace std;
const int maxn=505;
int n,a[maxn][maxn];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j];
int x=1,y=1;
bool run=false,dir=false;
cout<<a[x][y]<<' ';
while(x<n||y<n){
if(run){
do{
if(dir) cout<<a[++x][--y]<<' ';
else cout<<a[--x][++y]<<' ';
}while(1<x&&x<n&&1<y&&y<n);
run=false;
}
else{
if(x==1){
if(y<n) cout<<a[x][++y]<<' ';
else cout<<a[++x][y]<<' ';
}
else if(y==1){
if(x<n) cout<<a[++x][y]<<' ';
else cout<<a[x][++y]<<' ';
}
else if(x==n){
cout<<a[x][++y]<<' ';
}
else if(y==n){
cout<<a[++x][y]<<' ';
}
run=true;
dir=!dir;
}
}
return 0;
}
201412-3 集合竞价
这道题目数据范围很小,直接枚举就能过;
我用的是 复杂度更优的做法,即前缀和+双指针,相应的,代码也就更长。
P.S. 这种反复对vector进行各种操作,思路简单但步骤繁琐让人感觉像是在做 codeforces 的题目 …
代码:
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<db,int> P;
const int maxn=5e3+5;
P buy[maxn],sell[maxn];
ll bsum[maxn],ssum[maxn];
struct Record{
bool typ; db price; int num;
}in[maxn];
int main()
{
string s,type;
int id,tot=0;
while(cin>>type){
tot++;
if(type=="buy"){
in[tot].typ=0;
cin>>in[tot].price>>in[tot].num;
}
else if(type=="sell"){
in[tot].typ=1;
cin>>in[tot].price>>in[tot].num;
}
else{
cin>>id;
in[id].num=-1;
}
}
int scnt=0,bcnt=0;
for(int i=1;i<=tot;i++){
if(in[i].num==-1||in[i].num==0) continue;
if(in[i].typ){
scnt++;
sell[scnt].first=in[i].price;
sell[scnt].second=in[i].num;
}
else{
bcnt++;
buy[bcnt].first=in[i].price;
buy[bcnt].second=in[i].num;
}
}
sort(sell+1,sell+scnt+1);
sort(buy+1,buy+bcnt+1);
for(int i=1;i<=scnt;i++) ssum[i]=ssum[i-1]+sell[i].second;
for(int i=1;i<=bcnt;i++) bsum[i]=bsum[i-1]+buy[i].second;
db pri=0;
ll ans=0;
int idx=1;
for(int i=1;i<=bcnt;i++){
db p=buy[i].first;
while(idx<=scnt&&sell[idx].first<=p) idx++;
if(min(ssum[idx-1], bsum[bcnt]-bsum[i-1])>=ans){
pri=p;
ans=min(ssum[idx-1], bsum[bcnt]-bsum[i-1]);
}
}
printf("%.2f %lld\n",pri,ans);
return 0;
}
201412-4 最优灌溉
最小生成树裸题,直接 Kruskal:
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn=1e3+5;
const int maxm=1e5+5;
struct Edge{ int from,to,w; }e[maxm];
bool cmp(Edge e1,Edge e2){
return e1.w<e2.w;
}
int n,m,fa[maxn];
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main()
{
cin>>n>>m;
for(int i=0;i<m;i++)
cin>>e[i].from>>e[i].to>>e[i].w;
sort(e,e+m,cmp);
for(int i=1;i<=n;i++) fa[i]=i;
ll ans=0;
for(int i=0;i<m;i++){
int u=e[i].from, v=e[i].to;
u=find(u),v=find(v);
if(u==v) continue;
else{
ans+=e[i].w;
fa[u]=v;
}
}
cout<<ans<<'\n';
return 0;
}
201412-5 货物调度
费用流题目
没学过做不出来,直接看了yxc的录播学会了怎么建图,又找了几个比较好的网络流视频,熟悉了一些基础知识,这里mark一下:
算法进阶课(试听课)—— 网络流的基本概念
[算法竞赛入门] 网络流基础:理解最大流/最小割定理 (蒋炎岩)
代码:
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int maxn=705; // 100 * 7
const int maxm=(maxn*3 + 500*7*2) * 2;
int n,m,S,T;
struct Edge{ int to,cap,w,next; }e[maxm];
int head[maxn],idx;
int d[maxn],pre[maxn],incf[maxn]; // pre数组存储每个点在最短路上前一条边的编号; incf数组代表每个点的流量
bool vis[maxn]; // 用于spfa中判断是否在队列中
//a号点在第b天的编号
inline int get(int a,int b){
if(b==8) b=1;
return (a-1)*7+b;
}
// 建立一条a到b容量是c费用是d的边
void add(int a,int b,int c,int d){
e[idx].to=b, e[idx].cap=c, e[idx].w=d, e[idx].next=head[a], head[a]=idx++;
e[idx].to=a, e[idx].cap=0, e[idx].w=-d, e[idx].next=head[b], head[b]=idx++;
}
bool spfa(){
memset(d,0x3f,sizeof d); // 初始化每个点的距离
memset(incf,0,sizeof incf); // 初始化每个点的流量
memset(vis,0,sizeof vis);
queue<int> Q;
Q.push(S), d[S]=0, incf[S]=INF;
while(!Q.empty()){
int t=Q.front();
Q.pop();
vis[t]=false;
for(int i=head[t];~i;i=e[i].next){
int ver=e[i].to;
if(e[i].cap>0 && d[ver]>d[t]+e[i].w){
d[ver]=d[t]+e[i].w;
pre[ver]=i;
incf[ver]=min(incf[t], e[i].cap);
if(!vis[ver]){
Q.push(ver);
vis[ver]=true;
}
}
}
}
return incf[T]>0;
}
void EK(int& flow, int& cost){
flow=cost=0;
while(spfa()){
int t=incf[T];
flow+=t,cost+=t*d[T];
for(int i=T;i!=S;i=e[pre[i]^1].to){
e[pre[i]].cap-=t;
e[pre[i]^1].cap+=t;
}
}
}
int main()
{
cin>>n>>m;
S=0, T=7*n+1;
memset(head, -1, sizeof head);
for(int i=1;i<=n;i++){
int cap,cost;
for(int j=1;j<=7;j++){
cin>>cap;
add(S,get(i,j),cap,0);
}
for(int j=1;j<=7;j++){
cin>>cap;
add(get(i,j),T,cap,0);
}
cin>>cap>>cost;
for(int j=1;j<=7;j++)
add(get(i,j), get(i,j+1), cap, cost);
}
while(m--){
int u,v,cost;
cin>>u>>v>>cost;
for(int i=1;i<=7;i++){
add(get(u,i),get(v,i),INF,cost);
add(get(v,i),get(u,i),INF,cost);
}
}
int flow,cost;
EK(flow,cost);
cout<<cost<<'\n';
return 0;
}
总的来说Dinic算法还没学,而且基EK算法的网络流发展而来的费用流算法也没有学的很明白,感觉要完全掌握基础部分,达到独立切模板题还需要时间。