您的位置:首页 > 其它

ZOJ3206 Disaster Area Reconstruction ZOJ 3211 Dream City ZOJ 3212 K-Nice

2014-03-16 21:26 260 查看
省赛前热身做的题目,有难度的做出来的 放出来一下,印象更深刻,

在此打个小广告,都说咱们信息学院的不会享受生活不文雅,那么在此给一篇文章:让大家陶冶陶冶情操哈哈哈           http://www.sanwen.net/subject/3628849/

先来3206这道题目跟队友一起讨论WA了挺多把做出来的,题意是给你N个村庄,M条道路,道路是单向的,让你再建一条道路,使得这个图的连通分量最大,同时请输出最大连通分量 和 你所建的道路,

一开始完全想错,想到树链剖分,后来想暴力,看直接不行 就先进行强连通缩点,缩点完成后用DFS来进行查找,可惜超时,最后因为DFS发现有某些部分是重合查找,所有有些部分查找以后是可以递归返回值的,那么就是有递推的意思了,于是想到了树状DP,于是开始用树状DP,  WA了有七把吧,给出代码

总体思路就是强连通缩点,然后进行树状DP

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
#include<stdlib.h>
#define L(x) (x<<1)
#define R(x) (x<<1|1)
#define Mid(x,y) ((x+y)>>1)
using namespace std;
#define N 50010
//N为最大点数
#define M 501001
//M为最大边数
int n, m;//n m 为点数和边数

struct Edge{
int from, to, nex;
}edge[M];
int head
, edgenum;
void add(int u, int v){//边的起点和终点
Edge E={u, v, head[u]};
edge[edgenum] = E;
head[u] = edgenum++;
}

int DFN
, Low
, Stack
, ttop, Time; //Low[u]是点集{u点及以u点为根的子树} 中(所有反向弧)能指向的(离根最近的祖先v) 的DFN[v]值(即v点时间戳)
int taj;//连通分支标号,从1开始
int Belong
;//Belong[i] 表示i点属于的连通分支
bool Instack
;
vector<int> bcc
; //标号从1开始
vector<int>G
;
void tarjan(int u ,int fa){
DFN[u] = Low[u] = ++ Time ;
Stack[ttop ++ ] = u ;
Instack[u] = 1 ;

for (int i = head[u] ; ~i ; i = edge[i].nex ){
int v = edge[i].to ;
if(DFN[v] == -1)
{
tarjan(v , u) ;
Low[u] = min(Low[u] ,Low[v]) ;
}
else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ;
}
if(Low[u] == DFN[u]){
int now;
taj ++ ; bcc[taj].clear(); G[taj].clear();
do{
now = Stack[-- ttop] ;
Instack[now] = 0 ;
Belong [now] = taj ;
bcc[taj].push_back(now);
}while(now != u) ;
}
}

void tarjan_init(int all){
for(int i = 0;i<=all;i++)DFN[i]=-1,Instack[i] = 0;
ttop = Time = taj = 0;
for(int i=1;i<=all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意开始点标!!!
}

void suodian(){
for(int i = 0; i < edgenum; i++){
int u = Belong[edge[i].from], v = Belong[edge[i].to];
if(u!=v)G[u].push_back(v);
}
}
//////////////////////////////////////
int uu, vv, ans;
int siz
;
int ch
;
void dfs(int now, int TOP){
int v, TOP1=1000000;
int now1 = bcc[now][0], TOPP = bcc[TOP][0];
for(int i = 0;i < G[now].size();i++){
v = G[now][i];
if(siz[v]==0)dfs(v,TOP);
}
int maxn = 0;
for(int i =0;i<G[now].size();i++){
v = G[now][i];
if(maxn<siz[v])maxn = siz[v],TOP1=ch[v];
else if(maxn == siz[v])
TOP1=min(TOP1,ch[v]);
}
maxn += bcc[now].size();
if(G[now].size())ch[now] = TOP1;
siz[now] = maxn;
if(now==TOP)
{
now = bcc[now][0];
if(ans<maxn){ans = maxn;uu=TOP1;vv=now;}
else if(ans==maxn){
if(uu>TOP1){uu=TOP1; vv=now;}
else if(uu==TOP1)vv = min(vv,now);
}
}
}
void init(){
for(int i =1;i<=n;i++)head[i]=-1,siz[i] = 0; edgenum=0;
}

int main() {
int t;
int k, i,j;
scanf("%d",&t);
while(t--) {
scanf("%d%d",&n,&m);
if(m==0){printf("1\n1 2\n");continue;}
init();
while(m--){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
tarjan_init(n);
if(taj==1){printf("%d\n1 2\n",n);continue;}
suodian();
ans = 0;
for(i=1;i<=taj;i++)ans = max(ans,(int)bcc[i].size());
uu = 1, vv= 2;
for(i=1;i<=taj;i++)sort(bcc[i].begin(),bcc[i].end());
for(i=1;i<=taj;i++)if(G[i].size()==0)ch[i] = bcc[i][0], siz[i] = bcc[i].size();
for(i = 1;i<=taj;i++)if(siz[i]==0)
{
dfs(i,i);
}
printf("%d\n%d %d\n",ans,uu,vv);
}
return 0;
}
/*
99
2 1
1 2
2 0
2 2
1 2
2 1

5 4
1 3
2 3
3 4
3 5

8 9
1 2
2 3
3 1
3 4
4 5
5 6
6 4
6 7
8 7

6 6
1 2
2 3
3 1
4 5
5 6
6 4

8 7
1 2
2 3
3 1
4 5
5 6
6 7
7 8

*/


接下来3211

这是一道DP的题目,

意思是 给你N棵树,M天时间,一天砍一棵树,如果你有一天不砍,那么以后就不能再砍了,树是摇钱树,所以一开始树上长了一些金币,每天还会长金币,输入会给出每棵树初始金币和每天会生长的金币,问你第M天最多可以得到多少金币,我的DP方程是二维的,而且最终题目要的答案就是 dp
[m],那DP方程的含义就不用多说了 ,状态转移方程就是

dp[i][j] = max(dp[i-1][j],dp[i-1][j-1] + node[i].a + node[i].b * (j - 1));

在进行DP之前要进行排序,以那个 每天生长的金币数从小到大来排,具体原因我不清楚,我就是假设n ==m的时候,那肯定是 按照每天生长的金币数来排序的,所以就YY了一下 没想到 1A

#include<iostream>
#include<cstdio>
#include<list>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<cmath>
#include<memory.h>
#include<set>

#define ll long long

#define eps 1e-8

#define inf 0xfffffff

//const ll INF = 1ll<<61;

using namespace std;

typedef struct Node {
int a,b;
};

Node node[1000 + 5];
int dp[1000+ 5][1000 + 5];

void clear() {
memset(node,0,sizeof(node));
memset(dp,0,sizeof(dp));
}

bool cmp(Node x,Node y) {
return x.b < y.b;
}

int main() {
int t;
scanf("%d",&t);
while(t--) {
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%d",&node[i].a);
}
for(int i=1;i<=n;i++)
scanf("%d",&node[i].b);
sort(node + 1,node + 1 + n,cmp);
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
dp[i][j] = max(dp[i-1][j],dp[i-1][j-1] + node[i].a + node[i].b * (j - 1));
}
}
printf("%d\n",dp
[m]);
}
return 0;
}
接下来是3212,题意 给你一个N*M的矩阵,如果矩阵中有一个元素等于它上下左右四个相邻元素的和 的话,那就有一个nice,让你求出一个 k nice的矩阵,因为以前浙大月赛遇到过一道构造类的题目,所以这道题很快花了8分钟就A了,

说说我的思想过程,拿第一个案例来说明,我们先把矩阵全部写成 0,因为构造类的案例输出是不唯一的,只要符合题目要求即可

00000

00000

00000.

00000

那么此时 矩阵正中央的6个0是符合题目要求的,所以此时的矩阵是6 nice矩阵,第一个案例要求是3nice,那么我们破坏三个

01230

00000

00000

00000

此时的矩阵就符合了,那么破坏掉的数目就是 (n-2)*(m-2) - k,那么我们会发现 只要每一行除却首尾从1到(n-2)*(m-2) - k填上去即可

不放心再检验第二个案例

01230

04560

00000

00000

看看 是不是(n-2)*(m-2)-k刚好等于6,在不放心再多写几个

#include<iostream>
#include<cstdio>
#include<list>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<cmath>
#include<memory.h>
#include<set>

#define ll long long

#define eps 1e-8

#define inf 0xfffffff

//const ll INF = 1ll<<61;

using namespace std;

int main() {
int t;
int n,m,k;
scanf("%d",&t);
while(t--) {
scanf("%d %d %d",&n,&m,&k);
int tmp = (n - 2) * (m - 2) - k;
for(int i=0;i<n;i++) {
for(int j=0;j<m;j++) {
if(j == 0)
printf("0 ");
else if(j == m - 1)
printf("0\n");
else {
if(tmp == 0)
printf("0 ");
else
printf("%d ",tmp--);
}
}
}
}
return EXIT_SUCCESS;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: