[Sdoi2010 星际竞速]巧妙的费用流、有向无环图的最小权路径覆盖

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1927
【算法分析】
我感觉挺巧妙的。。。想了好一会儿。
首先注意到,每个点最终的入度都为1。
如果把最终停在这个点不再向后走,看成是向汇点连一条边的话,那么每个点的出度都为1。
然后呢?
我们相当于取N条边。这N条有向边除了和源点相连的以外,都是不重头,不重尾的。
这想到了什么?
匹配!
因为头尾可以看成二分图的两边~于是变成最小权匹配。但是由于与源点相连的可以重合,所以新增一个二分图X部的点,利用网络流给它赋予INF这么大的容量。
然后最小费用流完美解决。
【其它】1A。
Orz。。。好不容易又排第一。。。
1 24143 edward_mj 1004K 1917MS G++ 1.90K 2010-05-18 20:41:38 2 23595 nehzilrz 888K 1964MS G++ 1.68K 2010-05-16 13:54:28 3 23547 oimaster 824K 2091MS G++ 2.48K 2010-05-16 11:05:34 4 23681 gaoxin 1256K 2683MS Pascal 1.95K 2010-05-16 18:44:59 5 23196 root 868K 2714MS G++ 1.47K 2010-05-14 14:05:05 6 23813 FDhyc 1436K 2965MS Pascal 3.32K 2010-05-17 13:33:23 【CODE】
#include #include #include const int E=2000000;
const int N=1000005;
const int INF=1000000000;
struct edge{int x,y,c,w;edge *next,*op;}sp[E],*ls[N],*fa[N];
int n,m,S,T,e,cost;
int d[N],v[N],L[N];

void addedge(int x,int y,int c,int w){
e++;
sp[e].x=x; sp[e].y=y; sp[e].c=c; sp[e].w=w;
sp[e].next=ls[x]; ls[x]=&sp[e]; sp[e].op=&sp[e+1];
e++;
sp[e].x=y; sp[e].y=x; sp[e].c=0; sp[e].w=-w;
sp[e].next=ls[y]; ls[y]=&sp[e]; sp[e].op=&sp[e-1];
}

void input(){
scanf("%d%d",&n,&m);
S=n+n+2; T=S+1;
for (int i=1,x;i<=n;i++){
scanf("%d",&x);
addedge(S-1,i+n,1,x);
}
addedge(S,S-1,INF,0);
for (int i=1,x,y,w,tmp;i<=m;i++){
scanf("%d%d%d",&x,&y,&w);
if (x>y) {tmp=x;x=y;y=tmp;}
addedge(x,y+n,1,w);
}
for (int i=1;i<=n;i++){
addedge(S,i,1,0);
addedge(i+n,T,1,0);
}
}

bool spfa(){
int head=0,tail=1;
for (int i=1;i<=T;i++){
d[i]=INF;
v[i]=0;
}
d[S]=0; v[S]=1; L[1]=S;
while (head!=tail){
head++; if (head>=N) head=0;
for (edge *t=ls[L[head]];t;t=t->next)
if (t->c && d[t->x]+t->w d[t->y]=d[t->x]+t->w;
fa[t->y]=t;
if (!v[t->y]){
v[t->y]=1;
tail++; if (tail>=N) tail=0;
L[tail]=t->y;
}
}
v[L[head]]=0;
}
if (d[T]==INF) return false;
return true;
}

void change(){
int nf=INF;
for (int i=T;i!=S;i=fa[i]->x)
if (fa[i]->c nf=fa[i]->c;
cost+=nf*d[T];
for (int i=T;i!=S;i=fa[i]->x){
fa[i]->c-=nf;
fa[i]->op->c+=nf;
}
}

int main(){
input();
cost=0;
while (spfa()) change();
printf("%dn",cost);
}

[Sdoi2010 地精部落]动态规划

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1925
【算法分析】
F[i][j][k]表示第i段,小于当前选择的那个数的个数是j,k代表是下一个该上升还是下降,的方案数。
然后转移就行了。
然后需要用滚动数组优化。
【时间复杂度】O(n^2)
【空间复杂度】O(n)
【CODE】
#include #include #include #include using namespace std;
const int N=4205;
int n,mod;
int F[2][N][2];

void dp(){
int i,j,k,p,rest,Sum;
memset(F,0,sizeof(F));
for (i=0;i for (i=2;i<=n;i++){
p=i%2; rest=n-i;
for (j=0;j<=rest;j++){
F[p][j][0]=0;
F[p][j][1]=0;
}
// Deal up
Sum=0;
for (j=0;j<=rest;++j){
Sum+=F[1-p][j][1]; if (Sum>=mod) Sum%=mod;
F[p][j][0]=Sum;
}
// Deal down
Sum=F[1-p][rest+1][0];
for (j=rest;j>=0;–j){
F[p][j][1]=Sum;
Sum+=F[1-p][j][0]; if (Sum>=mod) Sum%=mod;
}
}
Sum=0;
for (i=0;i<=rest;i++){
Sum+=F[p][i][0]; if (Sum>=mod) Sum%=mod;
Sum+=F[p][i][1]; if (Sum>=mod) Sum%=mod;
}
cout << Sum << endl;
}

int main(){
cin >> n >> mod;
if (n==1) cout << 1 % mod << endl;
else dp();
}

[Sdoi2010 大陆争霸]二叉堆、拓扑序、畸形最短路

【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1922
【算法分析】
这个显然必须按照”保护图“的拓扑序来搞。
然后我就模拟那个”保护图“拓扑的过程。
不过期间用最小堆来存放结点的d[i]值。
堆中存放的都是入度为0,且尚未扩展的点。
然后每次取堆顶元素拿出来扩展。
扩展方法如下。
1、
首先把”保护图“中的边弄掉。(其实就是拓扑排序的过程)
然后如果出现可新加入的点,那么update一下他,然后把他放入堆。
update(i)的意思是:d[i]=min(max(limit,d[x]+w)) 其中limit为当前点的d值。
x为在”时间图“中有边x->i的点。w就是该边对应的权值了。
2、
在”时间图“里扩展,假如有我能到的点,而且他的入度以为0,那么可以min一下我走过去的时间。

然后最后输出d[n]。
实际上第一种扩展方式是第二种的漏掉的补充,YY一下就能明白。
【时间复杂度】O((n+m) lg n)
【其它】
泪奔。。。好久没排过第一了。时间是最快的。。。当然,其实挺接近。
然后最后一个是我徒弟~
他的方法完全暴力。
k次spfa,每次判整个d数组是否和前一次的不同,然后拓扑扩展一下,将本次在队列里的点的保护去除。
Orz。。。这样的都能过。。。
1 23953 edward_mj 2392K 326MS G++ 3.06K 2010-05-17 22:02:49 2 23587 nehzilrz 12700K 373MS G++ 1.29K 2010-05-16 13:51:06 3 23691 gaoxin 13488K 466MS Pascal 1.18K 2010-05-16 19:11:12 4 23360 oimaster 1060K 513MS G++ 1.56K 2010-05-15 15:50:58 5 23339 jsn1993 1244K 591MS G++ 1.10K 2010-05-15 07:53:14 6 23184 root 45784K 1152MS G++ 1.14K 2010-05-14 13:40:08 7 23188 FDhyc 45784K 1230MS G++ 1.14K 2010-05-14 13:56:02 8 23936 54525871 2064K 2013MS Pascal 2.48K 2010-05-17 21:12:32 【CODE】
#include #include #include #define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
typedef __int64 lld;
const int E=1000005;
const int N=3005;
const int INF=1000000000;
int n,m;
int du[N],zz[N];
lld d[N];

struct edge{int x,y,w;edge *next;};
struct Link_t{
int e;
edge sp[E],*ls[N];
void init(){
e=0;
for (int i=0;i }

void add(int x,int y,int w){
e++;
sp[e].x=x; sp[e].y=y; sp[e].w=w; sp[e].next=ls[x];
ls[x]=&sp[e];
}
}Graph,Protect,Graphop;

struct Heap_t{
int tot,a[N],tmp;

void swap(int x,int y){
tmp=a[x]; a[x]=a[y]; a[y]=tmp;
zz[a[x]]=x; zz[a[y]]=y;
}

void up(int k){
while (k>1 && d[a[k/2]]>d[a[k]]){
swap(k/2,k);
k/=2;
}
}

void down(int k){
int p;
while (k*2<=tot){
p=k*2;
if (p+1<=tot && d[a[p+1]] if (d[a[p]] swap(p,k);
k=p;
}else break;
}
}

void ins(int key){
a[++tot]=key;
zz[key]=tot;
up(tot);
}

void del(){
swap(1,tot);
zz[a[tot]]=0;
tot–;
if (tot<1) return;
down(1);
}

void repair(int p){
up(p);
down(p);
}
}heap;

void input(){
Graph.init();
Protect.init();
Graphop.init();
memset(du,0,sizeof(du));
scanf("%d%d",&n,&m);
for (int i=1,x,y,w;i<=m;i++){
scanf("%d%d%d",&x,&y,&w);
Graph.add(x,y,w);
Graphop.add(y,x,w);
}
for (int i=1,Num,x,j;i<=n;i++){
scanf("%d",&Num);
for (j=1;j<=Num;j++){
scanf("%d",&x);
Protect.add(x,i,0);
du[i]++;
}
}
}

void update(int p,lld limit){
for (edge *t=Graphop.ls[p];t;t=t->next)
if (max(d[t->y]+t->w,limit) d[p]=max(d[t->y]+t->w,limit);
}

void topsort(){
int i;
edge *t;

for (i=1;i<=n;i++) d[i]=(lld)(INF)*INF;
d[1]=0;
heap.tot=0;

for (i=1;i<=n;i++)
if (!du[i]) heap.ins(i);

while (heap.tot>0){
int x=heap.a[1];
heap.del();
for (t=Protect.ls[x];t;t=t->next){
du[t->y]–;
if (!du[t->y]){
update(t->y,d[x]);
heap.ins(t->y);
}
}
for (t=Graph.ls[x];t;t=t->next)
if (!du[t->y] && d[t->y]>d[t->x]+t->w){
d[t->y]=d[t->x]+t->w;
heap.repair(zz[t->y]);
}
}
}

int main(){
input();
topsort();
printf("%I64dn",d[n]);
return 0;
}

[ZOJ3340 The Stars My Destination]海伦公式和勾股定理的妙用

【题目大意】
空间坐标系中(x,y,z)中:
给出一个射线和N个点。
求出和这个射线最远的点和射线的距离是多少。
【算法分析】
首先围观一下点到直线的距离怎么搞?
+_+。。。直接弄垂线不好搞啊。
于是弄一个三角形面积。再除以底边的长度,就可以得到高了。
面积可以用海伦公式。这样就什么都不需要了,只需要点与点之间的长度计算。

然后再YY一下射线,如果是高飘出射线,那个特定的角就会变钝角。(划一下就可以知道哪个角)
然后利用勾股定理a^2+b^2#include #include #include #include const int N=100005;
struct Point{int x,y,z;}pl[N],p1,p2;
int n;
double ans;

void input(){
scanf("%d%d%d",&p1.x,&p1.y,&p1.z);
scanf("%d%d%d",&p2.x,&p2.y,&p2.z);
for (int i=1;i<=n;i++)
scanf("%d%d%d",&pl[i].x,&pl[i].y,&pl[i].z);
}

inline double Sqr(double x){return x*x;}
inline double max(double x,double y){
if (x>y) return x;
else return y;
}
inline double min(double x,double y){
if (x else return y;
}

inline double dis(Point A,Point B){
return sqrt(Sqr(A.x-B.x)+Sqr(A.y-B.y)+Sqr(A.z-B.z));
}

inline void swap(double &x,double &y){
double tmp=x; x=y; y=tmp;
}

inline bool DunJiao(double a,double b,double c){
if (Sqr(a)+Sqr(b) return false;
}

void solve(){
int i,j,k;
double p,S,a,b,c,now;
ans=0;
for (i=1;i<=n;i++){
a=dis(p1,pl[i]); b=dis(p1,p2); c=dis(p2,pl[i]);
p=0.5*(a+b+c);
S=sqrt(p*(p-a)*(p-b)*(p-c));
now=min(a,c);
if (!DunJiao(a,b,c))
now=min(now,S*2/b);
ans=max(ans,now);
}
printf("%.2lfn",ans);
}

int main(){
while (scanf("%d",&n)!=EOF){
input();
solve();
}
}

[ZOJ3335 Filtering]矩阵维护类型的题目

【题目大意】
给出一个a[i][j]。
然后ans[i][j]=以(i , j)为中心的一个m*n的矩形内所有数字的中位数。
如果m*n超出了边框。那么ans[i][j]=a[i][j]。
0<=a[i][j]<256。
【算法分析】
首先,注意到键值的范围为[0,255]。
我们容易想到弄一个桶来维护。或许桶里还可以搞个线段树什么的。
然后显然ans[i][j]和ans[i][j-1]所包含的数里,只有两列不同。于是我们可以在O(p*q*n)里维护一个所包含数字的桶。
有这个桶呢,暴力找中位数,那么复杂度是O(p*q*(n+256))。
但是依然MS可能超时。
于是我们可以加入一个优化:
ans[i][j]和ans[i][j-1]一般相差不是很远。
那么我们就在ans[i][j]的基础上,先前滑动和向后滑动,一般而言就可以很快出解了。
【时间复杂度】O(p*q*(n+256)) 一般达不到。
【CODE】
#include #include #include const int N=505;
int a[N][N],b[N][N],T[256];
int m,n,p,q,L;

void input(){
int i,j;
for (i=1;i<=m;i++)
for (j=1;j<=n;j++)
scanf("%d",&a[i][j]);
scanf("%d%d",&p,&q);
L=p*q/2+1;
}

int Cal(int x,int y){
int i,j;
for (i=x-p;i<=x+p;i++)
for (j=y-q;j<=y+q;j++)
T[a[i][j]]++;
j=0;
for (i=0;i<256;i++){
j+=T[i];
if (j>=L) return i;
}
}

void solve(){
p/=2; q/=2;
int i,j,k,pre,next;
for (i=1;i<=m;i++)
for (j=1;j<=n;j++)
b[i][j]=-10000;
bool first;
for (i=1+p;i+p<=m;i++){
memset(T,0,sizeof(T));
first=true;
for (j=1+q;j+q<=n;j++)
if (first){
first=false;
b[i][j]=Cal(i,j);
pre=0; next=0;
for (k=0;k for (k=b[i][j]+1;k<256;k++) next+=T[k];
}else{
int &t=b[i][j];
t=b[i][j-1];
for (k=i-p;k<=i+p;k++){
T[a[k][j-q-1]]–;
if (a[k][j-q-1] if (a[k][j-q-1]>t) next–;
T[a[k][j+q]]++;
if (a[k][j+q] if (a[k][j+q]>t) next++;
}
while (pre>=L){
next+=T[t];
t–;
pre-=T[t];
}
while (next>=L){
pre+=T[t];
t++;
next-=T[t];
}
}
}
}

void output(){
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++){
if (b[i][j]<0) b[i][j]=a[i][j];
printf("%d",b[i][j]);
if (j==n) putchar(‘n’);
else putchar(‘ ‘);
}
putchar(‘n’);
}

int main(){
while (scanf("%d%d",&m,&n)!=EOF){
input();
solve();
output();
}
}