JZWC【Day4】题解&总结
来源:互联网 发布:android编程权威指南3 编辑:程序博客网 时间:2024/05/17 04:57
怎么说呢,今天的题不难也不水,很考验人的思维,实为一套好题。但实现难度不高,有了思路很快就能打出来了。
T1 灌水
Description
有n个点,在点i和点j之间连边的费用为p[i,j],把每个点标记为关键点的费用为wi,求所有点都之间或间接的能到达一个关键点的最小费用。
Input
第一行:一个数n
第二行到第n+1行:第i+1行含有一个数wi
第n+2行到第2n+1行:第n+1+i行有n个被空格分开的数,第j个数代表pij。
Output
一个单独的数代表最小代价。
Sample Input
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
Sample Output
9
输出详解:
把第四个点标为关键,然后把其他的都连向那一个,这样就要花费3+2+2+2=9。
Data Constraint
1<=n<=300
1<=wi<=100000
1<=pij<=100000,pij=pji,pii=0
Solution
很神奇的一道题,我们发现如果没有关键点的限制,那么就只用最小生成树算法就可以了。现在我们多了一个限制,即每棵树上都要有一个关键点。那么我们考虑将第i个点标为关键点的状态转化到边上。加入一个新点,每个点向它连选其为关键点的费用,这样就可以用普通的最小生成树算法来解决了。
Code
#include<cstdio>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define M 95005#define N 305using namespace std;struct note{ int x,y,z;}a[M];int w[N],x,y,tot,f[N],ans,n;bool cmp(note x,note y) { return x.z<y.z;}int get(int x) { if (!f[x]) return x; return f[x]=get(f[x]);}int main() { scanf("%d",&n); fo(i,1,n) { scanf("%d",&x);a[++tot].x=0;a[tot].y=i;a[tot].z=x; } fo(i,1,n) fo(j,1,n) { scanf("%d",&x);a[++tot].x=i;a[tot].y=j;a[tot].z=x; } sort(a+1,a+tot+1,cmp); fo(i,1,tot) { x=get(a[i].x);y=get(a[i].y); if (x!=y) { ans+=a[i].z;f[x]=y; if (--n==0) break; } } printf("%d",ans);}
T2 炮兵阵地
Description
在一个n*m的地图上放炮兵,每个炮兵可以攻击上下左右两格的区域,如图 每一格有两种状态,P表示可放,H表示不可放,求在所有炮兵互相不能攻击到的情况下最多能放多少炮兵。
Input
文件的第一行包含两个由空格分割开的正整数,分别表示N和M; 接下来的N行,每一行含有连续的M个字符(‘P’或者‘H’),中间没有空格。按顺序表示地图中每一行的数据。
Output
文件仅在第一行包含一个整数K,表示最多能摆放的炮兵部队的数量。
Sample Input
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
Sample Output
6
Data Constraint
N<=100,M<=10
Solution
经典状压dp例题,预处理出每一行所有合法的状态,直接转移就好了。
Code
#include<cstdio>#include<cstring>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define N 105#define M 1025using namespace std;int a[N],f[2][M][M],n,m,mx,b[M],c[M],p,ans,tot;char s[15];bool check(int x) { return (!(x&(x<<1)))&&(!(x&(x<<2)))&&(!(x&(x>>1)))&&(!(x&(x>>2)));}int cout(int x) { int z; for(z=0;x;x>>=1) z+=(x&1); return z;}int main() { scanf("%d%d",&n,&m);mx=(1<<m)-1; fo(i,1,n) { scanf("%s",s+1); fo(j,1,m) if (s[j]=='H') a[i]+=1<<(j-1); }p=1; fo(i,0,mx) if (check(i)) { b[++tot]=i;c[tot]=cout(i); if (!(i&a[1])) f[0][0][i]=c[tot]; } fo(i,2,n) { fo(j,1,tot) fo(k,1,tot) if (f[1-p][b[j]][b[k]]) { int t=(mx-(b[j]|b[k]))&(mx-a[i]); fo(l,1,tot) if ((b[l]&t)==b[l]) f[p][b[k]][b[l]]=max(f[p][b[k]][b[l]], f[1-p][b[j]][b[k]]+c[l]); } p=1-p;memset(f[p],0,sizeof(f[p])); } fo(i,1,tot) fo(j,1,tot) ans=max(ans,f[1-p][b[i]][b[j]]); printf("%d",ans);}
T3 Islands and Bridges
Description
给出有n个点的一张无向图,每个点有一个价值v[i],求n个点的一个遍历序列,使得
(1)所有点的价值和
(2)序列中相邻两格点的价值之积的和
(3)若一个点和在它上一个点的上一个点有边相连,则有这三个点的价值之积的和
这些值的和最大,求最大值和方案数。
Input
输入第一行是一个整数Q,表示测试数据的数量。每个测试数据第一行输入两个整数N和M,分别表示点数和边数,接下来一行包含N个正整数,第i个数表示Vi,最后M行,每行两个数X,Y,表示点X和点Y之间有一条边直接相连。
Output
对于每个测试数据,输出一行,两个整数,第一个数表示最大价值,第二个数表示方案数,如果不存在路径,输出“0 0”
注意:一条路径可以反着走,我们认为这两条路径是同一条路径。
Sample Input
2
3 3
2 2 2
1 2
2 3
3 1
4 6
1 2 3 4
1 2
1 3
1 4
2 3
2 4
3 4
Sample Output
22 3
69 1
Data Constraint
Q<=20,N<=13,Vi<=100
Solution、
又一道状压dp例题,注意细节。
Code
#include<cstdio>#include<cstring>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define N 14#define M 10000#define ll long longusing namespace std;int ty,n,m,v[N],a[N][N],f[M][N][N],ans,tot,x,y,maxn;ll g[M][N][N],num;int main() { for(scanf("%d",&ty);ty;ty--) { scanf("%d%d",&n,&m);maxn=(1<<n)-1; memset(a,0,sizeof(a)); fo(i,1,n) scanf("%d",&v[i]); if (n==1) { printf("%d 1\n",v[1]);continue; } fo(i,1,m) scanf("%d%d",&x,&y),a[x][y]=a[y][x]=1; memset(f,0,sizeof(f));memset(g,0,sizeof(g)); fo(i,1,n) fo(j,1,n) if (i!=j&&a[i][j]) { x=1<<(i-1);y=1<<(j-1); f[x+y][i][j]=v[i]*v[j]+v[i]+v[j]; g[x+y][i][j]=1; } fo(i,0,maxn) fo(j,1,n) { x=1<<(j-1); if (!(i&x)) { fo(k,1,n) fo(l,1,n) if (f[i][k][l]&&a[l][j]) { tot=f[i][k][l]+v[j]+ v[l]*v[j]+a[k][j]*v[k]*v[l]*v[j]; if (tot>=f[i+x][l][j]) { if (tot==f[i+x][l][j]) g[i+x][l][j]+=g[i][k][l]; else g[i+x][l][j]= g[i][k][l];f[i+x][l][j]=tot; } } } }num=ans=0; fo(i,1,n) fo(j,1,n) if (f[maxn][i][j]>=ans) { if (f[maxn][i][j]==ans) num+=g[maxn][i][j]; else num=g[maxn][i][j];ans=f[maxn][i][j]; } printf("%d %lld\n",ans,num/2); }}
T4 奶牛的图片
Description
给出一个1..n的排列,每一次可以交换相邻两个数,求用最小的步数使得序列合法。一个合法的序列是指和[1..n]循环同构的序列。
Input
第1行:一个单独的数N
第2到n+1行:第i+1行上的数表示第i个数.
Output
一个整数,表示是原序列变为一个合法的序列的最小花费。
Sample Input
5
3
5
4
2
1
Sample Output
2
Data Constraint
1<=n<=100,000
Solution
再遇神题
首先你需要知道,一个1..n的排列,通过互换相邻两个数,使其变成[1..n]的最小步数为其的逆序对个数
然后你需要知道,O(n log n)的逆序对的求法。归并排序呀~树状数组呀~都行。
再然后,你需要找出[a..n,1..a-1]和[a+1..n,1..a]的步数之间的关系,那么你就可以过了。
发现这两个序列中的[a+1..a-1]这一段是相同的,那我们需要求出,原序列中a的位置不变,其余位置变成形如这样的序列的最小步数。设[a..n,1..a-1]的步数为k1,a在原序列中的位置为p[a],那么很明显就是k1-(p[a]-1),然后我们再把它变回第二个序列,步数为(n-p[a]),所以总代价就是k1-(p[a]-1)+n-p[a],即k1+n+1-2*p[a]。很明显,这就是最小答案,枚举一下目标状态,递推出来即可。
Code
#include<cstdio>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define N 100005#define ll long longusing namespace std;int a[N],n,b[N],c[N];ll ans,k;void mergesort(int l,int r) { if (l==r) return; int mid=(l+r)/2; mergesort(l,mid);mergesort(mid+1,r); int i=l,j=mid+1,tot=l; while (i<=mid&&j<=r) if (a[i]<=a[j]) b[tot++]=a[i++]; else b[tot++]=a[j++],k=k+mid-i+1; while (i<=mid) b[tot++]=a[i++]; while (j<=r) b[tot++]=a[j++]; fo(t,l,r) a[t]=b[t];}int main() { scanf("%d",&n); fo(i,1,n) scanf("%d",&a[i]),c[a[i]]=i;k=0; mergesort(1,n);ans=k; fo(i,1,n-1) k=k+n+1-2*c[i],ans=min(ans,k); printf("%lld",ans);}
果然说把整场比赛的分数都押到只有一个点的第三题是作死的行为~最后都没有时间打第二,四题了,第三题忘记开longlong而爆了0,最后只得了100分,我不服!!!
- JZWC【Day4】题解&总结
- JZWC【Day1】题解&总结
- JZWC【Day2】题解&总结
- JZWC【Day3】题解&总结
- day4总结
- Oracle day4 总结
- day4.26总结_Fragment
- 夏令营day4总结
- day4-java&oracle总结
- NOIP夏令营day4课程总结
- day4.08总结_UI控件
- day4.09总结_Menu 菜单
- day4
- day4
- day4
- day4
- DAY4
- DAY4
- 兔子出生总数问题
- C#之数组、集合与泛型
- 转折后的总结--2014年找工作
- ubuntu下安装RemixOS双系统(Android x86)
- SSH中的权限拦截器
- JZWC【Day4】题解&总结
- 2014找工作总结-机会往往留给有准备的人
- Python爬虫入门(7):正则表达式
- 使用SoapObject登入网站获取通行证
- 《Java编程思想》第七章 复用类
- Vigenère密码题解
- Maven的配置
- JavaBean与jsp的动作
- poj 2706 Connect