ACM日常训练日记——8.8(二分dp,最小生成树+克鲁斯卡尔算法和普利姆算法)

  • codeforces训练
  1. C. Hungry Games

本题大意就是找到最后g不等于0的区间个数。

主要思路:找前缀和第一次大于k的下标idx(二分),然后我们发现idx+1的方案数相当于把idx+1当作左端点来算,然后我们就想到dp[i]代表以i为左端点的方案数。

总结:在比赛的时候找到了idx,但是因为没有想到用dp来表示,所以是一直递归下去,然后因为一些边界问题还是没有写出来。

ps:本题因为需要用到后面的状态,所以我们从后往前二分来做。

一般这种区间的问题,就是以左端点或者右端点为起点,然后在较小的时间复杂度内找到符合条件的区间数。

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int dx[4]={0,0,-1,1};
int dy[4]={-1,1,0,0};
#define int long long
typedef pair<int,int> pii;
 
const int N=200010;
 
int s[N];
int a[N];
int n,k;
int dp[N];
 
void solve()
{
	cin>>n>>k;
	memset(s,0,sizeof s);
	memset(dp,0,sizeof dp);
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		s[i]=s[i-1]+a[i];
	}
	int ans=0;
	for(int i=n;i>=1;i--)
	{
		int l=i-1,r=n+1;
		while(l+1<r)
		{
			int mid=l+r>>1;
			if(s[mid]-s[i-1]>k)
			{
				r=mid;
			}else l=mid;
		}
		if(r==n+1)dp[i]+=n-i+1;
		else dp[i]=dp[r+1]+r-i;
	}
	for(int i=1;i<=n;i++)ans+=dp[i];
	cout<<ans<<endl;
}
															                                                                                                  
signed main()
{ 
    int good_luck_to_you;
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
   // good_luck_to_you=1; 
    cin>>good_luck_to_you;
    while(good_luck_to_you--)
    {
       solve();
    }
    system("pause");   
}
  1. C. Arrow Path
    DFS
// LUOGU_RID: 171384671
#include <bits/stdc++.h>
using namespace std;

#define  int long long

const int MOD = 1e9 + 7;
const int MAX_N = 3e5 + 5;
int n;
bool vis[3][200005];
char mp[3][200005];
vector<int> g[MAX_N];
bool st[MAX_N];
int qpow(int a, int b, int m) {
    int result = 1;
    a %= m;
    while (b > 0) {
        if (b & 1) {
            result = (result * a) % m;
        }
        a = (a * a) % m;
        b >>= 1;
    }
    return result;
}
int xx[]={0,0,1,-1};
int yy[]={1,-1,0,0};
bool inmp(int x,int y){
    return x>0&&x<3&&y>0&&y<n+1;
}

void dfs(int tx,int ty){
    vis[tx][ty]=true;
    if(tx==2 and ty==n){
        return;
    }
    for(int i=0;i<4;i++){
        int x=tx+xx[i];
        int y=ty+yy[i];
        if(inmp(x,y)&&!vis[x][y]){
            vis[x][y]=true;
            if(mp[x][y]=='>'&&y+1<=n&&!vis[x][y+1]){
                dfs(x,y+1);
            }
            vis[x][y]=false;
            if(mp[x][y]=='<'&&y-1>=1&&!vis[x][y-1]){
                dfs(x,y-1);
            }
            vis[x][y]=false;

        }
    }

}


int32_t main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=2;i++){
            for(int j=1;j<=n;j++){
                cin>>mp[i][j];
            }
        }
        memset(vis,false,sizeof(vis));
        dfs(1,1);
        if(vis[2][n])cout<<"YES"<<'\n';
        else cout<<"NO"<<'\n';
    }
    return 0;
}

3.C. Naming Company
友谊赛的题,博弈论精彩,你需要去考虑每一次头尾的比较,字符串一是否大于字符串二所有的数,还有顺序,
我补题的时候思路对了,但是后面想把状态结合再一起的时候没有考虑周全,看了队长的代码后恍然大悟了属于是
我们可以开两个deque然后保留需要的数放进去,每次比较就可以直接用q1的front或者是back和q2的去比较,这样
就能每次比较全部。精彩,实在是精彩

#include <bits/stdc++.h>
using namespace std;

#define  int long long
const int N = 1e8+10;
const int MOD = 1e9 + 7;
bool  f[1008611];
int prime[1008611];
int cnt=0;

int ma(int a,int b,int c){
    int t=max(a,b);
    return max(t,c);
}
int32_t main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    // cin>>t;
    while(t--){
        string a,b;
        cin>>a>>b;
            int n=a.length();
            sort(a.begin(),a.end());
            sort(b.begin(),b.end());
            int cnt=0;
        if(n==1){
            cout<<a.front();
            continue;
        }
            deque<char> q1, q2;
            for (int i = 0; i <(n+1)/2; ++i) {
                q1.push_back(a[i]);
            }
            for (int i = 0; i <n/2 ; ++i) {
                q2.push_front(b[n-i-1]);
            }
            int l=0;
            int r = n - 1;
            vector<char> ans(n);
            for(int i=0;i<n;i++) {
                if(q1.empty()){
                    ans[l++]=q2.front();
                    break;
                }
                if(q2.empty()){
                    ans[l++]=q1.front();
                    break;
                }
                if ((i & 1) == 0) {
                    if (q1.front() >= q2.back()) {
                        ans[r--]=q1.back();
                        q1.pop_back();
                    }else{
                        ans[l++]=q1.front();
                        q1.pop_front();
                    }
                }else{
                    if (q2.back() <= q1.front()) {
                        ans[r--] = q2.front();
                        q2.pop_front();
                    } else {
                        ans[l++] = q2.back();
                        q2.pop_back();
                    }
                }
            }
            for (auto i: ans) {
                cout << i;
            }
    }
    return 0;
}
  • nowcoder训练

    1.挖沟
    最小生成数的板子题
    克鲁斯卡尔算法和普利姆算法

最小生成树模板题。
prim算法:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2*500000+10;
const int maxnn = 100000+10;
struct sy{
    int to;
    int next;
    int w;
} edge[maxn];
int head[maxn];
struct Node{
    int number;
    int w;
    bool operator < (const Node &n) const {
        return w>n.w;
    }
};
priority_queue<Node> pq;
bool vis[maxnn];
int cnt = 0;
int n, m;

void add_edge(int x, int y, int w)
{
    edge[++cnt].next = head[x];
    edge[cnt].to = y;
    edge[cnt].w = w;
    head[x] = cnt;
}
//普利姆算法,利用贪心的原理求最小生成树
int prim()
{
    int ans = 0;
    pq.push({1, 0});
    while (pq.size())
    {
        Node node = pq.top();
        pq.pop();
        int number = node.number;
        int w = node.w;
        if (vis[number]) continue;
        ans += w;
        vis[number] = true;
        //遍历这个点的其他边,找出没有遍历过的加入
        for (int i=head[number];i;i = edge[i].next)
        {
            int next = edge[i].to;
//             cout<<next<<" "<<edge[i].w<<endl;
            if (vis[next]) continue;
            pq.push({next, edge[i].w});
        }
    }
    return ans;
}

int main()
{
    int x, y ,w;
    cin>>n>>m;
    for (int i=1;i<=m;i++)
    {
        cin>>x>>y>>w;
        add_edge(x, y, w);
        add_edge(y, x, w);
    }
    int ans = prim();
    cout<<ans;
    return 0;
}
克鲁斯卡尔算法:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2*500000+10;
struct Node{
    int x, y, w;
} node[maxn];

bool comp(Node n1, Node n2)
{
    return n1.w<n2.w;
}
int fa[100000+10];
int n, m;

int find(int x)
{
    return fa[x]==0?x:fa[x] = find(fa[x]);
}


int main()
{
    cin>>n>>m;
    for (int i=1;i<=m;i++)
    {
        cin>>node[i].x>>node[i].y>>node[i].w;
    }
    sort(node+1, node+1+m, comp);
    int ans = 0;
    for (int i=1;i<=m;i++)
    {
        int x = node[i].x;
        int y = node[i].y;
        int rootx = find(x);
        int rooty = find(y);
        if (rootx==rooty) continue;
        ans += node[i].w;
        fa[rootx] = rooty;
    }
    cout<<ans<<endl;
    return 0;
}

2.道路建设
还是最小生成树的板子

#include<bits/stdc++.h>
using namespace std;
const int maxn=10005,maxm=100005;

struct E{
    int from,next,to,dis;
}edge[maxm*2];

int c,n,m,u,v,w;
int head[maxn],cnt=0,fa[maxn];

int find(int x){
    if(fa[x]==x) return x;
    else return fa[x]=find(fa[x]);
}

void unite(int x,int y){
    fa[find(x)]=find(y);
}

void addedge(int from,int to,int dis){
    edge[++cnt].next=head[from];
    edge[cnt].from=from;
    edge[cnt].to=to;
    edge[cnt].dis=dis;
    head[from]=cnt;
}

bool cmp(E a,E b){
    return a.dis<b.dis;
}

int main(){
    scanf("%d%d%d",&c,&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
        addedge(v,u,w);
    }
    int tot=0,sm=0;
    for(int i=1;i<=m;i++) fa[i]=i;
    sort(edge+1,edge+1+cnt,cmp);
    for(int i=1;i<=cnt;i++){
        if(find(edge[i].to)!=find(edge[i].from)){
            sm+=edge[i].dis;
            unite(edge[i].to,edge[i].from);
            tot++;
        }
        if(tot==m-1) break;
    }
    if(sm<=c) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
    return 0;
} 
posted @ 2024-08-09 00:24  冬天的睡袋  阅读(8)  评论(0编辑  收藏  举报