码迷,mamicode.com
首页 > 其他好文 > 详细

NOIP2017解题报告

时间:2018-02-04 22:37:59      阅读:159      评论:0      收藏:0      [点我收藏+]

标签:typename   string   nod   algo   using   family   print   tle   状态   

啊不小心点发布了,懒得删了就这样吧,虽然还没写完,也不打算写了大概。

d1t1

结论题 没什么好说的

d1t2

模拟 没什么好说的

d1t3

70分算法其实比较好想。

没有0边,就跑最短路,然后按dis从小到大转移。

场上最后十分钟才发现单向边,就没时间考虑0边,并且相当于傻逼一样排了个序,水了60;

肯定不能直接排序呀  n*k*log爆了啊,只把n个点按dis排序,然后先枚举一个k即可,肯定是从k小的转移到大的。

然后0边重新建图,拓扑排序,看环上有没有可行解,有就输出-1。否则把拓扑序作为第二关键字排序,转移即可。

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<cmath>
const int N=100005;
const int M=200005;
typedef long long LL;
using namespace std;

int T,n,m,k,p;
LL dp[N][51];

template<typename T> void read(T &x) {
	char ch=getchar(); x=0; T f=1;
	while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
	if(ch==‘-‘) f=-1,ch=getchar();
	for(;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=x*10+ch-‘0‘; x*=f;
}

int fir[N],nxt[M],to[M],val[M],ecnt;
int fif[N],nxf[M],tf[M],vaf[M],ecnf;
void add(int u,int v,int w) {
	nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w;
	nxf[++ecnf]=fif[v]; fif[v]=ecnf; tf[ecnf]=u; vaf[ecnf]=w;
}
 
int fi[N],nx[M],tt[M],in[N],ec;
void Add(int u,int v) {
	nx[++ec]=fi[u]; fi[u]=ec; tt[ec]=v; in[v]++;
}

queue<int>que;
int ds[N],dt[N],vis[N],tps[N];
void spfa(int s,int d[],int fir[],int nxt[],int to[],int val[]) {
	d[s]=0;
	vis[s]=1;
	que.push(s);
	while(!que.empty()) {
		int x=que.front();
		que.pop();
		vis[x]=0;
		for(int i=fir[x];i;i=nxt[i]) 
			if(d[to[i]]>d[x]+val[i]) {
				d[to[i]]=d[x]+val[i];
				if(!vis[to[i]]) {
					vis[to[i]]=1;
					que.push(to[i]);
				}
			}
	}
}

int tpsort() {
	for(int i=1;i<=n;i++) if(!in[i]) que.push(i);
	int idd=0;
	while(!que.empty()) {
		int x=que.front();
		que.pop();
		tps[x]=++idd;
		for(int i=fi[x];i;i=nx[i]) {
			if(!(--in[tt[i]]))
				que.push(tt[i]);
		} 
	} 
	for(int i=1;i<=n;i++) 
		if(in[i]&&ds[i]+dt[i]<=ds[n]+k) 
			return 1;
	return 0;
}

struct node{
	int x;
	friend bool operator <(const node &A,const node &B) {
		return ds[A.x]==ds[B.x]?tps[A.x]<tps[B.x]:ds[A.x]<ds[B.x];
	}
}po[N*50];

void clear() {
	ecnt=ec=ecnf=0;
	memset(dt,127,sizeof(dt));
	memset(ds,127,sizeof(ds));
	memset(fi,0,sizeof(fir)); 
	memset(fir,0,sizeof(fir));
	memset(fif,0,sizeof(fif));
	memset(in,0,sizeof(in));
	memset(tps,0,sizeof(tps));
	memset(dp,0,sizeof(dp));
}

void work() {
	spfa(1,ds,fir,nxt,to,val); 
	spfa(n,dt,fif,nxf,tf,vaf);
	if(tpsort()) printf("-1\n");
	else {
		int tot=0;
		for(int i=1;i<=n;i++) 
			po[++tot].x=i;
		dp[1][0]=1;
		sort(po+1,po+tot+1);
		for(int y=0;y<=k;y++) {
		for(int o=1;o<=tot;o++) {
			int x=po[o].x;
			for(int i=fir[x];i;i=nxt[i]) 
				if((LL)ds[x]+y+val[i]+dt[to[i]]<=ds[n]+k) {
					(dp[to[i]][ds[x]+y+val[i]-ds[to[i]]]+=dp[x][y])%=p;
				}
		}
		}
		LL ans=0;
		for(int i=0;i<=k;i++) (ans+=dp[n][i])%=p;
		printf("%lld\n",ans);
	}
}

void init() {
	read(T);
	while(T--) {
		clear();
		read(n);
		read(m);
		read(k);
		read(p);
		for(int i=1;i<=m;i++) {
			int u,v,w;
			read(u); read(v); read(w);
			if(!w) Add(u,v);
			add(u,v,w);
		}
		work();
	}
}

#define DEBUG
int main() {
#ifdef DEBUG
	freopen("park.in","r",stdin);
	freopen("park.out","w",stdout);
#endif
	init();
	return 0;
}

  

 

d2t1

并查集

之前在长一考了一道有点像的二维的用并查集这样搞可以水80;

 

d2t2

一眼状压,然后不会。

推了半个小时prufer数列,并没有什么用。

自己对dp的理解还是太粗浅了。

一个可做的方法是,枚举层数,枚举状态,枚举补集,暴力转移。

你觉得十分不科学,然而事实是这样是可以得出正确答案的。

几个事。

1。不一定全部的状态或者转移都是最优的,只要最终可以达到最优解即可。

2。注意细节,如果层数枚举到12然后空间只开13你不就越界了嘛。。

3。省略一些不用的转移,比如dp==inf时直接略过,不然会死亡tle。

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<cmath>
#define inf 0x7fffffff
typedef long long LL;
using namespace std;
const int N=13;
int n,m,dis[N][N];
LL dp[N][1<<N]; 

template<typename T> void read(T &x) {
    char ch=getchar(); x=0; T f=1;
    while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
    if(ch==‘-‘) f=-1,ch=getchar();
    for(;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=x*10+ch-‘0‘; x*=f;
}

int q1[N],q2[N],t1,t2;
LL cal(int s,int t) {
    t1=t2=0;
    LL res=0;
    for(int i=1;i<=n;i++) {
        if(s&(1<<(i-1))) q1[++t1]=i;
        if(t&(1<<(i-1))) q2[++t2]=i;
    }
    for(int i=1;i<=t2;i++) {
        int tp=1e9;
        for(int j=1;j<=t1;j++) 
            tp=min(tp,dis[q2[i]][q1[j]]);
        res+=tp;
    } 
    return res;
}

void work() {
    int nn=(1<<n)-1; 
    LL ans=1e9;
    for(int i=0;i<=n;i++) 
        for(int j=0;j<=nn;j++)
            dp[i][j]=inf;
    for(int i=1;i<=n;i++) dp[1][1<<(i-1)]=0;
    for(int i=1;i<n;i++) {
        for(int s=1;s<=nn;s++) if(dp[i][s]!=inf){
            int bj=nn^s;
            for(int vv=bj;vv;vv=(vv-1)&bj) 
                dp[i+1][s|vv]=min(dp[i+1][s|vv],dp[i][s]+i*cal(s,vv));
        }
        ans=min(ans,dp[i][nn]);
    }
    ans=min(ans,dp[n][nn]);
    printf("%lld\n",ans);
}

void init() {
    read(n);
    read(m);
    memset(dis,127/3,sizeof(dis));
    for(int i=1;i<=m;i++) {
        int u,v,w;
        read(u); read(v); read(w);
        dis[u][v]=min(dis[u][v],w);
        dis[v][u]=dis[u][v];
    }
}

int main() {
#ifdef DEBUG
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#endif
    init();
    work();
    return 0;
}

  

d2t3

 

NOIP2017解题报告

标签:typename   string   nod   algo   using   family   print   tle   状态   

原文地址:https://www.cnblogs.com/Achenchen/p/7921808.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!