2019.7.14 并查集P1197 [JSOI2008]星球大战 说能过那是假的(动态规划) cometoj #c6 双倍快乐

星球大战

这题是使用了一个total变量持续记录连通块数目,极大减少了判断时间

一开始我直接用从0到n-1遍历找连通块,这样绝对超时

通过代码如下

#include<bits/stdc++.h>
using namespace std;
int fa[400005],q[400005],ans[400005],total;
vector<int> v[400005];
bool vis[400005];
int get(int x){
	if(x==fa[x]){
		return x;
	}
	return fa[x]=get(fa[x]);
}
void merge(int x,int y){
	fa[get(fa[x])]=get(fa[y]);
}
int main(){
	int n,m,t,x,y;
	scanf("%d%d",&n,&m);
	for(int i=0;i<=n-1;i++){
		fa[i]=i;
	}
	for(int i=0;i<m;i++){
		scanf("%d%d",&x,&y);
		v[x].push_back(y);
		v[y].push_back(x);
	}
	scanf("%d",&t);
	for(int i=0;i<t;i++){
		scanf("%d",&x);
		q[i]=x;
		vis[x]=1;
	}
	total=n-t;
	for(int i=0;i<=n-1;i++){
		if(!vis[i]){
			for(int j=0;j<v[i].size();j++){
				if(!vis[v[i][j]]&&get(i)!=get(v[i][j])){
					total--;
					merge(i,v[i][j]);
				}
			}
		}
	}
	ans[t]=total;
	reverse(q,q+t);
	for(int i=0;i<t;i++){
		vis[q[i]]=0;
		total++;
		for(int j=0;j<v[q[i]].size();j++){
			if(!vis[v[q[i]][j]]&&get(q[i])!=get(v[q[i]][j])){
				total--;
				merge(q[i],v[q[i]][j]);
			}
		}
		ans[i]=total;
	} 
	reverse(ans,ans+t);
	for(int i=0;i<=t;i++){
		printf("%d\n",ans[i]);
	}
	return 0;
}

A不了的
1、fa数组没初始化到4e5
2、数组没开大到4e5
3、算法不行

这题还能用图的特殊性质来代替我的vector,但是复杂度是一样的

说能过那是假的
题意很清楚

这题直接先记录Z的数量cntz
然后从头开始
如果是O,cnto++
如果是R ans+=cnto*cntz
如果是Z cntz–
很巧妙

/*
*@Author:   STZG
*@Language: C++
*/
#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
#define endl "\n"
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=100000+10;
const int M=100000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,p,l,r,u,v;
ll ans,cnt,flag,temp,sum,cot;
char ch[100007];
int main()
{
#ifdef DEBUG
    freopen("input.in", "r", stdin);
    //freopen("output.out", "w", stdout);
#endif
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    //cout.tie(0);
    //scanf("%d",&t);
    //while(t--){
    scanf("%s",ch);
    int len=strlen(ch);
    for(int i=0;i<len;i++)
        if(ch[i]=='Z')cnt++;
    for(int i=0;i<len;i++){
        if(ch[i]=='O')cot++;
        if(ch[i]=='R')ans+=cot*cnt;
        if(ch[i]=='Z')cnt--;
    }
    printf("%lld\n",ans);
    //}
 
#ifdef DEBUG
    printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
    //cout << "Hello world!" << endl;
    return 0;
}

接近凌晨
再写一题

双倍快乐

一题挺难想的动态规划

要求在一段序列里寻找两条可不连续的独立的不下降子序列
使得两条序列之和最大

看了几篇博客,都是没讲的很深入
我也想了很久

最后是通过调试想出的这个转移方程的意义

那么首先要明白

既然是两条子序列

我们就要用二维dp

那么以什么为维度呢?

就以每条子序列的 末端!

因为末端好穷举,而且遍历不会漏掉

所以我们的 i 从1开始到n
j从0开始,0代表其中一条序列为空序列
j<i意味着 i永远大于j
就不会重复列举了

此时如果num[i]>num[j]
那么num[i]就具备连接到num[j]后面的条件

然后一个一个链接就好

然后枚举k
总之挺难想的

dp[i][j]意思是两子序列分别以i,j结尾的最大序列之和

#include <bits/stdc++.h>

using namespace std;
int dp[600][600];
int num[600];
int main() {
    int n;
    scanf("%d",&n);


    num[0] = 0;
    for (int i=1;i<=n;i++) {
        scanf("%d",&num[i]);
    }


    memset(dp,0,sizeof(dp));
    int ans = 0;
    for (int i=1;i<=n;i++) {
        for (int j=0;j<i;j++) {
            if (num[i] >= num[j]) {
                for (int k=0;k<i;k++) {
                    dp[i][k] = max(dp[i][k],dp[j][k] + num[i]);
                    dp[k][i] = max(dp[k][i],dp[k][j] + num[i]);
                    ans = max(ans,dp[i][k]);
                }
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}
发布了61 篇原创文章 · 获赞 8 · 访问量 2468

猜你喜欢

转载自blog.csdn.net/weixin_43982216/article/details/95867969
今日推荐