叉姐的魔法训练(第八课)---- 幽默术
--------------
一 MSTDP
POJ 3538 Domestic Networks
有n个点m条边,和5型6型两种线,每米花费p5、p6,各有q5、q6米。
每条边要么用5型连接要么用6型,或不连接。
求将n个点连接的最小花费。与最小花费的方案。
首先求出最小生成树。
在对树上的边进行dp。
f[ i ][ j ] 表示 前i条路用5型j米的最小花费。
f[i][j]=f[i-1][j]+c*p6 (第i条路不使用5型)
f[i][j]=f[i-1][j-c]+c*p5 (第i条路使用5型)
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <vector> #define rst(x) memset(x,0,sizeof(x)) #define clr(x,a) memset(x,a,sizeof(x)) #define sz(x) int(x.size()) using namespace std; const int maxn=1111; const int maxm=11111; const int INF=0x3f3f3f3f; int n,m; struct Edge{ int u; int v; int w; int id; bool operator<(const Edge& rhs) const{ return w<rhs.w; } }road[maxm]; struct Node{ int w; int id; Node(int w,int id){ this->w=w; this->id=id; } bool operator<(const Node& rhs) const{ return id<rhs.id; } }; int pa[maxn]; void makeSet(int n){ for (int i=0;i<=n;i++) pa[i]=i; } int findSet(int x){ if (x!=pa[x]) pa[x]=findSet(pa[x]); return pa[x]; } void unionSet(int x,int y){ x=findSet(x); y=findSet(y); if (x!=y) pa[x]=y; } int p5,q5,p6,q6; int sum,num; vector<Node>a; int f[1005][10005]; int path[1005][10005]; void dfs(int cnt,int y){ if (!cnt) return; if (path[cnt][y]!=y){ dfs(cnt-1,path[cnt][y]); printf("%d 5\n",a[cnt-1].id); } else{ dfs(cnt-1,y); printf("%d 6\n",a[cnt-1].id); } } int main() { while (~scanf("%d%d",&n,&m)){ makeSet(n); a.clear(); for (int i=0;i<m;i++){ scanf("%d%d%d",&road[i].u,&road[i].v,&road[i].w); road[i].id=i+1; } scanf("%d%d%d%d",&p5,&q5,&p6,&q6); sort(road,road+m); sum=0; for (int i=0;i<m;i++){ int u=road[i].u; int v=road[i].v; int w=road[i].w; if (findSet(u)!=findSet(v)){ unionSet(u,v); a.push_back(Node(road[i].w,road[i].id)); sum+=w; } } if (sz(a)!=n-1||sum>q5+q6){ printf("Impossible\n"); continue; } sort(a.begin(),a.end()); memset(f,0x3f,sizeof(f)); int total=0; f[0][0]=0; for (int i=1;i<=sz(a);i++){ total+=a[i-1].w; int c=a[i-1].w; for (int j=q5;j>=0;j--){ if (total-j<=q6&&f[i-1][j]!=INF&&f[i-1][j]+c*p6<f[i][j]){ f[i][j]=f[i-1][j]+c*p6; path[i][j]=j; } if (j>=c&&f[i-1][j-c]!=INF&&f[i-1][j-c]+c*p5<f[i][j]){ f[i][j]=f[i-1][j-c]+c*p5; path[i][j]=j-c; } } } int ans=INF,y=-1; for (int i=0;i<=q5;i++){ if (ans>f[sz(a)][i]){ ans=f[sz(a)][i]; y=i; } } if (ans==INF) printf("Impossible\n"); else{ printf("%d\n",ans); dfs(sz(a),y); } } return 0; }
-------------------------------
二 构造方案
POJ 3566 Building for UN
题解摘自网络:
题目大意:要建新的联合国总部,每个国家都有若干相连的房间,请你设计一种方案,使每个国家的都有房子能够与其他国家的某个房间相邻,使这两个国家可以讨论秘密国事。用字母表示这些国家。
下面这样的方案就能满足题目的要求,而且能够满足各种n。例如,n=4:
ABCD
AAAA
ABCD
BBBB
ABCD
CCCC
ABCD
DDDD
相当于每个国家在每层楼各有一个办公室,从纵向看,一个竖条都是一个国家。每个国家在某层都有一个阳台,通向其他国家的办公室。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=1111; int a[2][maxn]; char list[maxn]; int main() { int n,h,w,l; for(int i=0;i<26;i++) list[i]='A'+i; for(int i=0;i<26;i++) list[26+i]='a'+i; scanf("%d",&n); h=n,w=n,l=2; printf("%d %d %d\n",h,w,l); for(int i=0;i<n;i++) { for(int j=0;j<n;j++) printf("%c",list[i]); printf("\n"); for(int j=0;j<n;j++) printf("%c",list[j]); if (i!=n-1) printf("\n\n"); else printf("\n"); } return 0; }
-------------------------------
三 SG分解
POJ 3537 Crosses and Crosses
摘自学姐博客:在第I个位置放一个X,即可分为两个子游戏,I-3和n-I-2
#include<iostream> #include<cstdlib> #include<stdio.h> #include<memory.h> using namespace std; int sg[2100]; int dfs(int n) { if(n<0) return 0;//n<0 if(sg[n]>=0) return sg[n]; bool g[2001]={0}; for(int i=1;i<=n;i++) { int t=dfs(i-3)^dfs(n-i-2); g[t]=1; } for(int i=0;;i++) if(g[i]==0) return sg[n]=i; } int main() { memset(sg,-1,sizeof(sg)); int n; while(scanf("%d",&n)!=EOF) { if(dfs(n)) puts("1"); else puts("2"); } }
-------------------------------